WEEK4 周记 作业A题——贪心算法_DDL的恐惧
一、题意
1.简述
有 n 个作业,每个作业都有自己的 DDL,如果没有在 DDL 前做完这个作业,那么老师会扣掉这个作业的全部平时分。如何安排做作业的顺序,才能尽可能少扣一点分。
2.输入格式
输入包含T个测试用例。输入的第一行是单个整数T,为测试用例的数量。
每个测试用例以一个正整数N开头(1<=N<=1000),表示作业的数量。
然后两行。第一行包含N个整数,表示DDL,下一行包含N个整数,表示扣的分。
3.输出格式
对于每个测试用例,您应该输出最小的总降低分数,每个测试用例一行。
4.样例
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
Output
0
3
5
Hint
上方有三组样例。
对于第一组样例,有三个作业它们的DDL均为第三天,ZJM每天做一个正好在DDL前全部做完,所以没有扣分,输出0。
对于第二组样例,有三个作业,它们的DDL分别为第一天,第三天、第一天。ZJM在第一天做了第一个作业,第二天做了第二个作业,共扣了3分,输出3。
二、算法
主要思路
对于第i天,只要ddl大于等于i的任务都能够在这一天完成,所以我们只需要从这些满足条件的任务中选一个分值最大的即可。
还需要注意的是,如果有两个任务
t
1
t_1
t1和
t
2
t_2
t2分值相同,且两者都能够在第i天进行,但是
t
1
t_1
t1的ddl要大于
t
2
t_2
t2,那么,这里应该安排
t
2
t_2
t2,因为
t
1
t_1
t1可能能够在后面的空闲的某一天进行,而
t
2
t_2
t2就不能了。
所以我们采取的策略是,按日期从后往前遍历,尽量将遍历经过的每一天都安排上任务,当然要安排能安排的任务中分值最大的(这个时候可以不管ddl,因为是从后往前遍历)。
从最后一个ddl所在的日期开始,我们每到达一天,就将ddl在该天的任务加入一个大根堆(以分值作为唯一比较大小的标准)中,然后取出堆顶元素,这个任务就是安排在这一天执行的任务。
如果遍历完第一天大根堆中还有任务,那堆中任务的分值之和就是我们最终要被扣掉的分数,这个分数是最小的。
该算法的时间复杂度为 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))。
三、代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
struct pr{
int a;
int b;
bool operator<(const pr& p)const
{
return a>p.a;
}
};
pr pp[1001];
int main()
{
int T;
scanf("%d",&T);
int i,j;
for(i=0;i<T;i++)
{
int n;
scanf("%d",&n);
for(j=0;j<n;j++)scanf("%d",&pp[j].a);
for(j=0;j<n;j++)scanf("%d",&pp[j].b);
sort(pp,pp+n);//从大到小
priority_queue<int> p;
int ddl=pp[0].a;//只需从最大的ddl所在的日期开始往前遍历
j=0;
for(;ddl;ddl--)
{//从后往前遍历天数
while(pp[j].a==ddl&&j<n)
{
p.push(pp[j].b);
j++;
}
if(!p.empty())p.pop();
}
int sum=0;
while(!p.empty())
{
sum+=p.top();
p.pop();
}
printf("%d\n",sum);
}
return 0;
}