2.1 DDL安排的问题
题目大意
给出一组任务的截止日期和对应的分数,所有的任务一天内均可完,一天也只能完成一个任务,如何安排才能使损失的分数最少?
思路
这个题的贪心指标就是分数。为了使分数最大,我们优先安排分数高的任务。针对每一项任务t,将其安排在他的截止日期之前,才能得分。安排越靠近t的截止日期,就能在保证分数的同时,尽可能的降低它挤占别的任务时间的可能。
贪心策略
按照分数高低安排每一项任务,对于一项任务,从其截止日期开始向前找,若有时间空闲则安排,若没有空闲时间,则该项任务无法完成,分数损失。
代码
#include<bits/stdc++.h>
using namespace std;
struct ddl
{
int time;
int score;
bool operator<(const ddl& o) const{
return score>o.score;//贪心指标
}
};
int main()
{
// freopen("in.txt","r",stdin);
int T; scanf("%d",&T);
int n;
ddl d[2000];
int loss=0;
int t[2000];
int flag=0;
for(int i=0;i<T;i++)
{
loss=0;
for(int i=0;i<1500;i++) t[i]=-1;
scanf("%d",&n);
for(int j=0;j<n;j++)
{
scanf("%d",&d[j].time);
}
for(int j=0;j<n;j++)
{
scanf("%d",&d[j].score);
}
sort(d,d+n);
for(int j=0;j<n;j++)
{
flag=0;
for(int k=d[j].time;k>0;k--)
{
if(t[k]==-1)
{
t[k]=j;
flag=1;
break;
}
}
if(flag==0) loss+=d[j].score;
}
printf("%d\n",loss);
}
return 0;
}
策略优化
以上方法是一个 O ( n 2 ) O(n^2) O(n2)级别的算法,重复扫描了好多已经被使用的时间点,造成浪费。我们只需要对每一个时间点进行枚举,找出截止日期在这个时间点之后的,分数最高的任务,将其安排在该时间点就可以了,省去重复扫描时间点的过程。时间点的范围,就是最晚的ddl和最早的ddl之间的时间,取分数最高的任务用最大堆实现
优化代码
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
struct ddl
{
int t,score;
bool operator < (const ddl& d) const
{
return score<d.score;
}
}d[1000099];
bool cmp(const ddl & a,const ddl &b)
{
return a.t<b.t;
}
priority_queue<ddl> pq;
//时刻维护一个大根堆,使之产生ddl在这一天之后的,分数最大的ddl
int t[1000099];
int main()
{
int T; int n;
// freopen("in.txt","r",stdin);
scanf("%d",&T);
memset(t,0,sizeof t);
for(int i=0;i<T;i++)
{
long long sum=0;
long long real=0;
while(!pq.empty()) pq.pop(); // 初始化
scanf("%d",&n);
for(int j=0;j<n;j++)
scanf("%d",&d[j].t);
for(int j=0;j<n;j++)
{
scanf("%d",&d[j].score);
sum+=d[j].score;
}
sort(d,d+n,cmp); //按ddl从小到大排序
int m=n-1;
for(int k=d[n-1].t;k>0;k--)
{
for(;d[m].t>=k&&m>=0;m--) //寻找日期大于等于k的 ddl
pq.push(d[m]);
if(!pq.empty())
{
real+=pq.top().score; //将score最大的,且时间大于等于k的任务安排
pq.pop();
}
}
cout<<sum-real<<endl;
}
return 0;
}