题目链接:http://acm.uestc.edu.cn/#/problem/show/1218
给出n根木棒的长度和价值,最多可以装在一个长 l 的容器中,相邻木棒之间不允许重叠,且两边上的木棒,可以伸一半的长度在容器外,求最大价值量
01背包是取和不取。那这里我们可以把容器长度 l x 2,筷子长度 x 2,就变成了最多两个筷子取一次(伸一半在外面),其余的要么取两次,要么不取。
普通01背包,一维dp[i]表示消耗体积i所得到的最大的价值。
那么这里dp[i][k]表示消耗体积i并有k(k <= 2)个在外面所得到的最大的价值。
1 //#pragma comment(linker, "/STACK:102400000, 102400000") 2 #include <algorithm> 3 #include <iostream> 4 #include <cstdlib> 5 #include <cstring> 6 #include <cstdio> 7 #include <vector> 8 #include <cmath> 9 #include <ctime> 10 #include <list> 11 #include <set> 12 #include <map> 13 using namespace std; 14 typedef long long LL; 15 typedef pair <int, int> P; 16 const int N = 4e3 + 5; 17 LL dp[N][5]; //dp[i][k]表示使用i体积有k个木板在露在外面的最大价值 18 int w[N]; 19 LL v[N]; 20 21 int main() 22 { 23 int t, n, g; 24 scanf("%d", &t); 25 for(int ca = 1; ca <= t; ++ca) { 26 scanf("%d %d", &n, &g); 27 LL ans = 0; 28 for(int i = 1; i <= n; ++i) { 29 scanf("%d %lld", w + i, v + i); 30 w[i] <<= 1; 31 ans = max(ans, v[i]); 32 } 33 g <<= 1; 34 memset(dp, 0, sizeof(dp)); 35 for(int i = 1; i <= n; ++i) { 36 for(int j = g; j >= w[i]/2; --j) { //一维背包类似,说明下面只能取一次 37 for(int k = 0; k <= 2; ++k) { 38 if(w[i] <= j) //全放在里面 39 dp[j][k] = max(dp[j - w[i]][k] + v[i], dp[j][k]); 40 if(k >= 1) //有一半在外面 41 dp[j][k] = max(dp[j - w[i]/2][k - 1] + v[i], dp[j][k]); 42 } 43 } 44 } 45 for(int i = 0; i <= 2; ++i) { 46 ans = max(ans, dp[g][i]); 47 } 48 printf("Case #%d: %lld\n", ca, ans); 49 } 50 return 0; 51 }