HDU 5543 Pick The Sticks:01背包变种
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5543
题意:
给你N个金条和一张长度为L的桌子。每个金条长度为a[i],价值为w[i]。金条只能在桌子上横着摆一排,并且只要金条的重心(中心)在桌子上,就可以放。问你在桌子上能够摆的金条的最大总价值。
题解:
首先表示状态:
考虑到第i个金条,在这之前已经占用了j的长度,在k个端点摆了金条。即:dp[i][j][k]
如何转移(逆推):
对于第i个金条,有三种决策:摆在桌子中央、摆在桌子端点处、不摆。
(1)摆在中央:dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-a[i]][k])
(2)摆在端点:dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-a[i]/2][k-1])
(3)不摆: dp[i][j][k] = max(dp[i][j][k], dp[i-1][j][k])
求dp:枚举i,j,k即可。
注:(1)由于空间限制,dp[i][j][k]去掉第一维[i],枚举j时从大到小枚举。
(2)由于计算金条长度的一半时会出现浮点数,所以在读入时就将所有a[i]*=2,并且L*=2。
AC Code:
#include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #define MAX_N 1005 #define MAX_L 4005 #define MAX_K 5 using namespace std; int n,l,t; long long ans; long long a[MAX_N]; long long w[MAX_N]; long long dp[MAX_L][MAX_K]; void read() { ans=0; cin>>n>>l; l*=2; for(int i=0;i<n;i++) { cin>>a[i]>>w[i]; a[i]*=2; ans=max(ans,w[i]); } } void solve() { memset(dp,0,sizeof(dp)); for(int i=0;i<n;i++) { for(int j=l;j>=a[i]/2;j--) { for(int k=0;k<3;k++) { if(j-a[i]>=0) dp[j][k]=max(dp[j][k],dp[j-a[i]][k]+w[i]); if(k-1>=0) dp[j][k]=max(dp[j][k],dp[j-a[i]/2][k-1]+w[i]); ans=max(ans,dp[j][k]); } } } } void print() { cout<<ans<<endl; } int main() { cin>>t; for(int cas=1;cas<=t;cas++) { cout<<"Case #"<<cas<<": "; read(); solve(); print(); } }