天天看點

ZOJ4019——貪心&&DP

題目

連結

大意:有一個容量為$c$的背包,有$n$個$s_1$類物體,價值都為$k_1$,體積分别為$s_{1,1}, s_{1,2}, \cdots, s_{1,n}$,有$m$個$s_2$類物體,價值都為$k_2$體積分别為$s_{2,1}, s_{2,2}, \cdots, s_{2,m}$,求背包能裝下的最大價值。(價值的計算方法:$k_i * (c - s_i)$)

分析

背包問題,但又要 先做貪心的處理,為什麼可以貪心呢?因為有這樣一個事實,對于同一類物品,肯定是優先放體積小的,因為體積小r就大,是以先對兩類物品按照體積分别排序。

是以最終選的物品的結果肯定是第一類物品的前i項,第二類物品的前j項 $(i,j >= 0)$

是以我們可以很輕松地定義DP中的“狀态”了。定義$dp[i][j]$為取了第一類物品的前$i$項,第二類物品的前$j$項 所獲得的價值。

狀态轉移方程 :

$dp[i][j] = max \{  dp[i-1][j] + (C - Sum1[i] -  Sum2[j]  )*k1, \ \  dp[i][j-1] + (C - Sum2[j]  -  Sum1[i] )*k1 \}$

其中$Sum1 $是第一類物品體積字首和,$Sum2$ 是第二類物品體積字首和。

1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 const int maxn = 2000 + 10;
 6 const int maxm = 2000 + 10;
 7 int k1, k2, cap, n, m;
 8 int s1[maxn], s2[maxm];
 9 ll  sum1[maxn], sum2[maxm], dp[maxn][maxm];
10 
11 int main()
12 {
13     int T;
14     scanf("%d", &T);
15     while(T--)
16     {
17         scanf("%d%d%d", &k1, &k2, &cap);
18         scanf("%d%d", &n, &m);
19         for(int i = 1;i <= n;i++)  scanf("%d", &s1[i]);
20         for(int i = 1;i <= m;i++)  scanf("%d", &s2[i]);
21         sort(s1+1, s1+n+1);
22         sort(s2+1, s2+m+1);
23         for(int i = 1;i <= n;i++)  sum1[i] = sum1[i-1] + s1[i];  //可以看作隻從一者取,也可看做初始化邊界
24         for(int i = 1;i <= m;i++)  sum2[i] = sum2[i-1] + s2[i];  //
25 
26         ll ans = -1;
27         for(int i=1; i <= n;i++)
28         {
29             if(cap < sum1[i])  break;
30             dp[i][0] = dp[i-1][0] + (cap - sum1[i]) * k1;
31             ans = max(ans, dp[i][0]);
32         }
33         for(int i=1; i <= m;i++)
34         {
35             if(cap < sum2[i])  break;
36             dp[0][i] = dp[0][i-1] + (cap - sum2[i]) * k2;
37             ans = max(ans, dp[0][i]);
38         }
39 
40         for(int i = 1;i <= n;i++)
41             for(int j = 1;j <= m;j++)
42             {
43                 if(cap < sum1[i]+sum2[j])  break;
44                 dp[i][j] = max(dp[i-1][j] + k1 * (cap - sum1[i] - sum2[j]), dp[i][j-1] + k2 * (cap - sum1[i] - sum2[j]));
45                 ans = max(ans, dp[i][j]);
46             }
47         printf("%lld\n", ans);
48     }
49     return 0;
50 }      

參考連結:

 1. 分析 https://www.cnblogs.com/czsharecode/p/9606768.html

 2. 代碼 https://blog.csdn.net/qq_41156122/article/details/79855315

個性簽名:時間會解決一切