連結:http://acm.hdu.edu.cn/showproblem.php?pid=2844
這道題的意思也是看了許久才明白過來
給你n和m分別代表n種銀幣,要買的東西不高於m元
然後給你2*n個資料,前n個是價值,後n個是個數
然後問你,重新組合這些硬幣,能組合出多少組不大於m的組合?
就是1到m有幾種情況。
這道題,考慮到有兩個情況:1、物品可拿有n件 2、物品可拿數量有上限
所以我們考慮使用多重背包。
這裡又要考慮使用多重背包的二進位最佳化,否則三個迴圈,會逾時。
那麼最重要的便是動態方程了,對於這道題我們怎麼列動態方程呢?
我們這樣分析:
我們的目的是在dp中儲存金錢的值,以便我們去統計大於m的值
所以dp中便是存金錢的值,那麼dp的下標呢?
當然是用硬幣的價值
因為:
每一個更新一個硬幣,那麼一個硬幣肯定有自己的價值
比如是1或者是2,我們肯定沒辦法去改變這個價值(總不能給你2元硬幣,你算1元吧?)
所以每次更新,我們只需要在用硬幣值做下標
然後就可以將動態方程列出來了:
dp[i]=max(dp[i],dp[i-value]+weight); //只是模板
AC代碼如下:
#include<iostream>#include<stdio.h>using namespace std;int dp[100010];//注意數組大小int value[2000],num[2000];int n,m;int max(int a,int b){if(a>b)return a;elsereturn b;}void ZeroOnePack(int value,int weight){int i;for(i=m;i>=value;i--)dp[i]=max(dp[i],dp[i-value]+weight);}void CompletePack(int value,int weight){int i;for(i=value;i<=m;i++)dp[i]=max(dp[i],dp[i-value]+weight);}void MultiplePack(int value,int weight,int num){if(value*num>=m) //轉化為完全背包CompletePack(value,weight);else //二進位最佳化{int k=1;while(k<=num){ZeroOnePack(k*value,k*weight);num-=k;k<<=1;}ZeroOnePack(num*value,num*value);}}int main(){while(scanf("%d %d",&n,&m)){if(n+m==0)break;int i;for(i=1;i<=m;i++) dp[i]=-10000000; //複製為最小 dp[0]=0;for(i=0;i<n;i++) scanf("%d",&value[i]);for(i=0;i<n;i++) scanf("%d",&num[i]);for(i=0;i<n;i++)MultiplePack(value[i],value[i],num[i]); //多重背包int ans=0;for(i=1;i<=m;i++) //統計{if(dp[i]>0) ans++;}printf("%d\n",ans);}return 0;}
最近開始更新部落格了,自己做題的心得也會拉上來,希望你看的時候,給俺留個言,加個人氣撒~