HDU 3591 The trouble of Xiaoqian(多重背包+全然背包)
HDU 3591 The trouble of Xiaoqian(多重背包+全然背包)
http://acm.hdu.edu.cn/showproblem.php?
pid=3591
題意:
有一個具有n種貨幣的貨幣系統, 每種貨幣的面值為val[i]. 如今小傑手上拿著num[1],num[2],…num[n]個第1種,第2種…第n種貨幣去買價值為T(T<=20000)的商品, 他給售貨員總價值>=T的貨幣,然後售貨員(可能,假設小傑給的錢>T,那肯定找錢)找錢給他. 售貨員每次總是用最少的硬幣去找錢給小傑. 如今的問題是: 小傑買價值T的商品時, 他給售貨員的硬幣數目+售貨員找他的硬幣數目最少等於多少?
分析:
我們令dp1[j]==x表示小傑給售貨員價值j的硬幣時, 須要最少x個硬幣. 我們令dp2[j]==x表示售貨員給小傑價值j的硬幣時, 須要最少x個硬幣.
那麽前一個問題就是一個多重背包問題(由於小傑的硬幣有限度), 而第2個問題是全然背包問題(售貨員硬幣無限).
終於我們所求為: min( dp1[T+i]+dp2[i]) 當中 i屬於[0,20000-T].
對於第一個多重背包問題:
我們令dp1[i][j]==x表示用前i種硬幣構成j金錢時, 最少須要x個硬幣.
初始化: dp1全為INF且dp1[0][0]=0.
對於第i種硬幣, 我們要分情況處理:
假設val[i]*num[i]>=20000, 那麽就做一次全然背包.
假設val[i]*num[i]<20000, 那麽就把該物品看出新的k+1種物品,然後做k+1次01背包.
終於我們所求為dp1[n][j]這維數組就是我們之前說的dp1[j].
對於第二個全然背包問題:
我們令dp2[i][j]==x表示用前i種硬幣構成j金錢時, 最少須要x個硬幣.
初始化: dp2全為INF 且dp2[0][0]=0.
狀態轉移: dp2[i][j] = min( dp2[i-1][j] , dp2[i][j-val[i]]+1 ) //sum是求和
前者表示第i種貨幣一個都不用, 後者表示第i種貨幣至少用1個.
終於所求: dp2[n][j]這維數組是我們上面所求的dp2[j].
終於讓i從T+1到20000遍歷一邊, 找出min( dp1[T] , dp1[i]+dp2[i-T] )的值.
AC代碼:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define INF 1e8 const int maxn=100+5; int n;//n種貨幣 int T;//商品金額 int val[maxn];//每種貨幣面值 int num[maxn];//每種貨幣數目 int dp1[20000+5]; int dp2[20000+5]; //1次01背包過程 void ZERO_ONE_PACK(int *dp,int cost,int sum) { for(int i=20000;i>=cost;i--) dp[i] = min(dp[i],dp[i-cost]+sum);//註意這裏是+sum,而不是+1 } //1次全然背包過程 void COMPLETE_PACK(int *dp,int cost) { for(int i=cost;i<=20000;i++) dp[i] = min(dp[i],dp[i-cost]+1); } //1次多重背包過程 void MULTIPLY_PACK(int *dp,int cost,int sum) { if(cost*sum>=20000) { COMPLETE_PACK(dp,cost); return ; } int k=1; while(k<sum) { ZERO_ONE_PACK(dp,cost*k,k); sum-=k; k*=2; } ZERO_ONE_PACK(dp,cost*sum,sum); } int main() { int kase=0; while(scanf("%d%d",&n,&T)==2) { //註意退出,否則WA if(n==0 && T==0) break; //讀取輸入 for(int i=1;i<=n;i++) scanf("%d",&val[i]); for(int i=1;i<=n;i++) scanf("%d",&num[i]); //初始化 for(int i=0;i<=20000;i++) dp1[i]=dp2[i]=INF; dp1[0]=dp2[0]=0; //遞推 for(int i=1;i<=n;i++) MULTIPLY_PACK(dp1,val[i],num[i]); for(int i=1;i<=n;i++) COMPLETE_PACK(dp2,val[i]); //輸出結果 int ans=dp1[T]; for(int i=T+1;i<=20000;i++) ans=min(ans, dp1[i]+dp2[i-T]); printf("Case %d: %d\n",++kase,ans==INF?-1:ans); } return 0; }
HDU 3591 The trouble of Xiaoqian(多重背包+全然背包)