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
", dp[T]);
35     }
36     return 0;
37 }
原文地址:https://www.cnblogs.com/titicia/p/5355677.html