题目链接:http://poj.org/problem?id=1276
一道很有意思的题目。
做完这道题后发现了很多神奇的东西,原来多重背包问题还可以转化成完全背包问题。
另外,也更多理解了动态规划,稍微明白了一点动归和搜索的差别,还需要继续努力。
别人的解题报告:http://blog.csdn.net/lyy289065406/article/details/6648102
dp[j]记录的是“最接近状态j且<=j”的状态值,即dp[j]<=j
dp的状态是从前向后更新,所以后面的状态会累积前面的状态。
具体解释来说,每个状态都是初始值,然后开始第一次更新,在0~cash-1中如果有一个状态更新,那么由于cash的初始值依然为0,所以cash必然会更新。
在之后的每次更新中,如果不更新dp[cash],那么dp[cash]就会一直保持原来状态。
另外一个就是计数器,感觉也是很神奇的东西。
在二重循环进行之前初始化计数器,也是很难理解的一个地方吧,关键在于cnt[j]=c[j-d[i]]+1;
这里也是一个动态转移方程。
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int INF=0x3f3f3f3f; const int maxn=100010; int cash,N; int n[15],d[15]; int dp[maxn],cnt[maxn]; int main(){ #ifndef ONLINE_JUDGE freopen("test.in","r",stdin); freopen("test.out","w",stdout); #endif while(~scanf("%d%d",&cash,&N)){ for(int i=1;i<=N;i++) scanf("%d%d",&n[i],&d[i]); memset(dp,0,sizeof(dp)); for(int i=1;i<=N;i++){ memset(cnt,0,sizeof(cnt)); for(int j=d[i];j<=cash;j++){ if(dp[j]<dp[j-d[i]]+d[i]&&cnt[j-d[i]]<n[i]){ dp[j]=dp[j-d[i]]+d[i]; cnt[j]=cnt[j-d[i]]+1; } } } printf("%d\n",dp[cash]); } return 0; }