题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5543
题目大意:有n个金条,每个金条有长度和价值,给一个长度为L的容器,当金条在容器两端的时候,只要重心在容器内也可以放下,问最多能获得的价值。
思路:01背包,但是重心怎么处理,最好的肯定是边界情况也就是刚好金条刚好能伸出一半,或者直接只取一根金条然后放在容器里面。
多开一位 dp[i][j][k]代表当前选第i个背包容量为j有k个超出边缘的金条时所能获得的最大价值
状态转移方程 dp[i][j][2]=max(dp[i-1][j-w[i]][2]+v[i],dp[i-1][j-w[i]/2][1]+v[i],dp[i][j][2])
dp[i][j][1]=max(dp[i-1][j-w[i]][1]+v[i],dp[i-1][j-w[i]/2][0]+v[i],dp[i][j][1])
dp[i][j][0]=max(dp[i-1][j-w[i]][0]+v[i],dp[i][j][0])
如果没有第一维i的话,那么j和k都要从大到小搞,很明显dp[j][1]是会影响到dp[j][2]的
处理整除的情况很简单,容器和金条长度都*2就好了,这样就能保证一定整除
最后不要忘了放一个的情况要特判一下
1 #include <stdio.h> 2 #include <iostream> 3 #include <string.h> 4 #include <algorithm> 5 using namespace std; 6 const int maxn=4010; 7 long long dp[maxn][3]; 8 long long v[maxn],w[maxn]; 9 void init(){ 10 memset(dp,0,sizeof(dp)); 11 } 12 void beibao(int pos,int V) { 13 for(int i=V;i>=w[pos]/2;i--) { 14 dp[i][2]=max(dp[i-w[pos]/2][1]+v[pos],dp[i][2]); 15 if(i>=w[pos]) 16 dp[i][2]=max(dp[i-w[pos]][2]+v[pos],dp[i][2]); 17 } 18 19 for(int i=V;i>=w[pos]/2;i--){ 20 dp[i][1]=max(dp[i-w[pos]/2][0]+v[pos],dp[i][1]); 21 if(i>=w[pos]) 22 dp[i][1]=max(dp[i-w[pos]][1]+v[pos],dp[i][1]); 23 } 24 for(int i=V;i>=w[pos];i--) 25 dp[i][0]=max(dp[i-w[pos]][0]+v[pos],dp[i][0]); 26 } 27 void solve(int T) { 28 init(); 29 30 int n,l; 31 scanf("%d %d",&n,&l); 32 l<<=1; 33 long long ans=0; 34 for(int i=1;i<=n;i++) { 35 scanf("%lld %lld",&w[i],&v[i]); 36 w[i]<<=1; 37 ans=max(ans,v[i]); 38 } 39 for(int i=1;i<=n;i++) { 40 beibao(i,l); 41 } 42 ans=max(ans,max(dp[l][0],max(dp[l][1],dp[l][2]))); 43 printf("Case #%d: %lld ",T,ans); 44 } 45 int main() { 46 int T; 47 scanf("%d",&T); 48 for(int i=1;i<=T;i++) solve(i); 49 }