[HDU] 1789 Doing Homework again
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=1789
方法:
既然一个作业超期了,不管超期多久都只扣一次固定的学分。又因为每个作业只要一天,所以可以看出n个作业n天就可以做完,那些截至日期大于n天的就不用考虑了。只考虑截至日期在n天内的。可以这样考虑,设截至日期在n天内的作业如果全部按时完成的话得到的学分的和是sum,再设一个实际获得学分t_sum(初始0),然后一个作业按时完成 t_sum就加上学分,超期的就不管,然后把原来的问题转变为在截至日期在n天内的作业中安排一个顺序使得获得的学分之和t_sum最大,然后用sum-t_sum得到扣除的最少学分,这样得到原来问题的解。
设D[i]为一个截至日期,D[i].values截至日期是D[i]的作业有哪些,然后从第一天开始计算,计算第D[i]前必须做完的作业中选择D[i]个作业来做能获得的最大学分,计算过程为,首先计算截止期是第一天的作业,从D[1].values选择出最高学分那个,当然只选1个,然后把它并入D[2].values,再选出2个最高学分的作业,然后再将这两个并入D[3].values,再选出3个最高分的哪个。。。。最后在D[n].values中选择最高分的n个作业,将学分相加得到t_sum。
问题解决了。
代码:
#include <iostream> #include <queue> using namespace std; int n; struct Course { int deadLine; int get; }; struct DeadLine { priority_queue<int> values; }; Course courses[1001]; DeadLine deadlines[1001]; int main() { int tc=0; scanf("%d",&tc); while(tc>0) { scanf("%d",&n); int sum = 0; for(int i =1;i<=n;i++) scanf("%d",&courses[i].deadLine); for(int i =1;i<=n;i++) scanf("%d",&courses[i].get); priority_queue<int> no_values ; for(int i =1;i<=1000;i++) deadlines[courses[i].deadLine].values = no_values; for(int i =1;i<=n;i++) { if(courses[i].deadLine<=n) { sum+=courses[i].get; deadlines[courses[i].deadLine].values.push(courses[i].get); } } int re =0; for(int i=1;i<=n;i++) { int t_re = 0; priority_queue<int> t_values = deadlines[i].values; if(!deadlines[i].values.empty()) { int j=0; while(j<i && !deadlines[i].values.empty()) { t_re+=deadlines[i].values.top(); deadlines[i].values.pop(); j++; } } re = re>t_re ?re :t_re; bool foundOne =false; int k=i; while(k<n) { if(!deadlines[k+1].values.empty()) { foundOne =true; break; } k++; } if(foundOne) { int j=0; deadlines[i].values = t_values; while(j<i && !deadlines[i].values.empty()) { deadlines[k+1].values.push(deadlines[i].values.top()); deadlines[i].values.pop(); j++; } i=k; } } cout<<sum-re<<endl; tc--; } return 0; }
感想:模拟,注意代码中基于概率优化的部分