zoj 3689 Digging 贪心+01背包
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4972
题意:总共有N个任务,共有T天。每个任务有ti, si表示需要ti天完成,当能完成任务的情况下(剩余天数t >= ti ),将获得黄金剩余天数t*si。
思路:
这道题和普通的01背包稍微有点不同。需要先排序。
想了一下什么情况下的01背包需要先排序,其实就是动态规划的后效性问题。
考虑如果只有2个任务,如果这两个任务交换顺序对结果没有影响的情况下,就不需要排序。
如果交换顺序对结果有影响,就需要排序。
比如普通的背包,总体积是V,物体A的价值是VA,体积是WA。物体B的价值是VB,体积是WB。
如果VA+VB<=V,那么A和B都可以放入背包。
当A先放入B后放入,占的体积是VA+VB,获得收益是WA+WB。
当B先放入A后放入,占的体积是VA+VB,获得收益是WA+WB,没有变化。那么这种情况就不需要先贪心排序。
而本题中假设总天数是T,任务A为ta, sa,任务B为tb, sb。如果ta+tb<=T,那么两个任务都可以做完。
如果先做任务A,再做任务B,获得的收益是T*sa + (T-ta)*sb。
如果先做任务B,再做任务A,获得的收益是T*sb + (T-tb)*sa。这种情况下两者不同,所以需要先贪心排序。
由于DP方程为 dp[i][j] = max(dp[i-1][j], dp[i-1][j-node[i].t]+j*node[i].s);
相当于已经算出了dp[i-1][j-node[i].t]再来计算dp[i][j]。t越大获得的收益越多。所以越后算的即t越大的会获得比较大的收益。
那么在排序的时候,需要T*sa + (T-ta)*sb < T*sb + (T-tb)*sa,即把小的放前面。
化简得-ta*sb < -tb*sa。
如果不清楚可以把DP过程打出来试这两组样例:
2 10
1 2
2 1
2 10
2 1
1 2
1 #include <bits/stdc++.h> 2 using namespace std; 3 int N, T; 4 int dp[10010]; 5 struct Node 6 { 7 int t, s; 8 }node[3010]; 9 bool cmp(Node n1, Node n2) 10 { 11 return -n1.t*n2.s < -n1.s*n2.t; 12 } 13 int main() 14 { 15 // freopen("in.txt", "r", stdin); 16 // freopen("out.txt", "w", stdout); 17 while(~scanf("%d%d", &N, &T)) 18 { 19 for(int i = 1; i <= N; i++) 20 { 21 scanf("%d%d", &node[i].t, &node[i].s); 22 } 23 sort(node+1, node+1+N, cmp); 24 memset(dp, 0, sizeof(dp)); 25 int ans = 0; 26 for(int i = 1; i <= N; i++) 27 { 28 for(int j = T; j >= node[i].t; j--) 29 { 30 // cout<<i<<" "<<j<<" "<<" "<<node[i].t<<" "<<dp[j]<<" "<<dp[j-node[i].t]+j*node[i].s<<endl; 31 dp[j] = max(dp[j], dp[j-node[i].t]+j*node[i].s); 32 } 33 } 34 printf("%d\n", dp[T]); 35 } 36 return 0; 37 }