[HDOJ5543]Pick The Sticks(DP,01背包)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5543

题意:往长为L的线段上覆盖线段,要求:要么这些线段都在L的线段上,要么有不超过自身长度一半的部分在线段外面,最多有两条这样的线段(在两头)。

dp(i,j,k)表示前i个线段覆盖在长度为j的线段上,期中有k个线段不完全在这个线段上的最大价值。考虑线段长度的奇偶问题,所以事先把L和其他线段长度乘2,以便操作。所以枚举所有线段,一般情况,就是01背包的问题,dp(i,j,k)=max(dp(i,j,k), dp(i-1,j-w(i),k)+v(i))。当枚举到k不是0的时候,还需要更新两端放的情况:dp(i,j,k)=max(dp(i,j,k),dp(i-1,j-w(i)/2,k-1)+v(i))。直接取w(i)/2是没有关系的,因为我们在这里希望可以贪心地尽可能地少占用当前L上的长度。

还有一个trick:L=1的时候这么搞。这时L当成一个支点,只能放一个。所以要提前处理所有线段的最大价值。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 const int maxn = 5010;
 6 int w[maxn], v[maxn];
 7 int n, L;
 8 LL dp[maxn][3];
 9 LL ret;
10 
11 int main() {
12   freopen("in", "r", stdin);
13   int T, _ = 1;
14   scanf("%d", &T);
15   while(T--) {
16     scanf("%d %d", &n, &L);
17     memset(dp, 0, sizeof(dp));
18     ret = 0; L <<= 1;
19     for(int i = 1; i <= n; i++) {
20       scanf("%d %d", &w[i], &v[i]);
21       w[i] <<= 1;
22       ret = max(ret, (LL)v[i]);
23     }
24     for(int i = 1; i <= n; i++) {
25       for(int j = L; j >= w[i]/2; j--) {
26         for(int k = 0; k <= 2; k++) {
27           if(j >= w[i]) dp[j][k] = max(dp[j][k], dp[j-w[i]][k]+v[i]);
28           if(k) dp[j][k] = max(dp[j][k], dp[j-w[i]/2][k-1]+v[i]);
29            ret = max(ret, dp[j][k]);
30         }
31       }
32     }
33     printf("Case #%d: %lld
", _++, ret);
34   }
35   return 0;
36 }
原文地址:https://www.cnblogs.com/kirai/p/5955455.html