题目
ZJM 有 n 个作业,每个作业都有自己的 DDL,如果 ZJM 没有在 DDL 前做完这个作业,那么老师会扣掉这个作业的全部平时分。
所以 ZJM 想知道如何安排做作业的顺序,才能尽可能少扣一点分。
请你帮帮他吧!
Input
输入包含T个测试用例。输入的第一行是单个整数T,为测试用例的数量。
每个测试用例以一个正整数N开头(1<=N<=1000),表示作业的数量。
然后两行。第一行包含N个整数,表示DDL,下一行包含N个整数,表示扣的分。
Output
对于每个测试用例,您应该输出最小的总降低分数,每个测试用例一行。
Sample Input
3
3
3 3 3
10 5 1
3
1 3 1
6 2 3
7
1 4 6 4 2 4 3
3 2 1 7 6 5 4
Sample Output
0
3
5
Hint
上方有三组样例。
对于第一组样例,有三个作业它们的DDL均为第三天,ZJM每天做一个正好在DDL前全部做完,所以没有扣分,输出0。
对于第二组样例,有三个作业,它们的DDL分别为第一天,第三天、第一天。ZJM在第一天做了第一个作业,第二天做了第二个作业,共扣了3分,输出3。
解题思路
这道题用贪心算法解。首先我们把题目给出的任务数据多关键字排序,第一是截止时间降序,第二是分值降序。
然后我们从最后一天开始往前安排,每天首先把符合条件的任务,即DDL大于等于当前天数的所有任务加入到优先级队列中,然后每天从优先级队列中取出时间DDL最靠近的分值最大的一天。最后,统计剩余任务的分值并输出。
程序代码
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<stdio.h>
#include<list>
#include<queue>
#include<algorithm>
#include <functional>
using namespace std;
struct node { //DDL节点
int t;
int v;
node(int tt, int vv) :t(tt), v(vv) {
}
bool operator<(const node& n)const {
if (v != n.v)return v < n.v;
return t < n.t;
}
bool operator>(const node& n)const {
if (v != n.v)return v > n.v;
return t > n.t;
}
};
int main() {
int t;
cin >> t;
for (int t1 = 0; t1 < t; t1++) { //获取输入的DDL
int n;
cin >> n;
int* t = new int[n]; //截止时间
int* v = new int[n]; //扣的分
for (int i = 0; i < n; i++) {
scanf("%d", &t[i]);
}
for (int i = 0; i < n; i++) {
scanf("%d", &v[i]);
}
for (int i = 0; i < n; i++) { //排序,第一按DDL降序,第二按扣分降序排
for (int j = i; j < n; j++) {
if (t[i] < t[j]) {
int tmp1 = t[i];
int tmp2 = v[i];
t[i] = t[j];
v[i] = v[j];
t[j] = tmp1;
v[j] = tmp2;
}///
else if (t[i] == t[j]) {
if (v[i] < v[j]) {
int tmp1 = t[i];
int tmp2 = v[i];
t[i] = t[j];
v[i] = v[j];
t[j] = tmp1;
v[j] = tmp2;
}
}
}
}
int current = 0;
priority_queue<node> p1; //优先级队列存还没完成的任务项
for (int i = n; i > 0; i--) {
while (t[current] >= i && current < n) { //加入符合条件的作业项
p1.push(node(t[current], v[current]));
current++;
}
if (!p1.empty()) { //每过一天,完成一项任务
p1.pop();
}
}
int remains = 0;
while (!p1.empty()) { //统计剩余的作业项
remains += p1.top().v; //扣分
p1.pop();
}
cout << remains << endl; //输出
}
return 0;
}