题目描述:

ZJM 有 n 个作业,每个作业都有自己的 DDL,如果 ZJM 没有在 DDL 前做完这个作业,那么老师会扣掉这个作业的全部平时分。

所以 ZJM 想知道如何安排做作业的顺序,才能尽可能少扣一点分。

请你帮帮他吧!

Input:

输入包含T个测试用例。输入的第一行是单个整数T,为测试用例的数量。

每个测试用例以一个正整数N开头(1<=N<=1000),表示作业的数量。

然后两行。第一行包含N个整数,表示DDL,下一行包含N个整数,表示扣的分。

Output:

对于每个测试用例,您应该输出最小的总降低分数,每个测试用例一行。

思路:

这是一道贪心的题目,扣分最少即得分最多,因此只需要解决如何得分最多就可以了。将n个作业排序,排序规则为按照分数由大到小,然后从第1个作业开始遍历至n,对于第i个作业,从ddl[i]开始往前面扫描,看是否能将其安排下,若能,则所得分数+=v[i],若不能,则抛弃此作业。从第i个作业的ddl前面扫描,可使得当前的作业对其他作业的影响最小,因此此贪心方案是正确的。

遍历了每个作业,并从其ddl开始向前面扫描,所以总的时间复杂度是O(n*max(ddl)),在此题目中,由于ddl最大值和n最大值相同,因此复杂度为O(n^2)

对于数据的加强,O(n^2)显然过于复杂,现在更新算法,使其时间复杂度为O(nlogn)。在上面的算法中,枚举了每一个作业,试图将每个作业安排到某一天,是从作业安排时间的角度出发的。而在下面的算法中,考虑在每一天安排某个作业,即从时间安排作业的角度出发。显然,最后的时间为max(ddl),因此需要预处理出ddl的最大值。然后从这一天开始向前遍历天数。对于第i天,安排的显然是ddl位于这天之后,且得分最多的作业。要想得到得分最多的作业,可使用优先队列进行实时的更新。算法遍历每一天,这个复杂度是n(题目中的n和ddl数量级相同),优先队列处理的时间复杂度为logn级别,因此总的时间复杂度为O(nlogn)

下面是数据简单时的代码:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int T,n,ans,sum;
bool flag[1010000];
struct node
{
int d;
int v;
bool operator <(const node &o)const
{
return v>o.v;
}
}a[1010];
int main()
{
cin>>T;
while(T--)
{
memset(flag,0,sizeof(flag));
cin>>n;sum=0,ans=0;
for(int i=1;i<=n;i++)
cin>>a[i].d;
for(int i=1;i<=n;i++)
{
cin>>a[i].v;
sum+=a[i].v;
}
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)
{
for(int j=a[i].d;j>=1;j--)
if(!flag[j])
{
flag[j]=1;
ans+=a[i].v;
break;
}
}
ans=sum-ans;
cout<<ans<<endl;
}
return 0;
}