POJ 3260 The Fewest Coins(多重背包+完全背包)
來源:程序員人生 發布時間:2014-11-21 08:18:57 閱讀次數:2709次
POJ 3260 The Fewest Coins(多重背包+完全背包)
http://poj.org/problem?id=3260
題意:
John要去買價值為m的商品. 現在的貨幣系統有n種貨幣,對應面值為val[1],val[2]…val[n]. 然后他身上每種貨幣有num[i]個. John必須付給售貨員>=m的金錢, 然后售貨員會用最少的貨幣數量找錢給John.
問你John的交易進程中, 他給售貨員的貨幣數目+售貨員找錢給他的貨幣數目 的和最小值是多少?
分析:
本題與POJ 1252類型:
http://blog.csdn.net/u013480600/article/details/40454963
假定John付款總額為S時的貨幣數目為T1, 售貨員找錢 (S-m) 的貨幣數目為T2. 我們要使得T1+T2最小, 那末自然T1和T2也必須各自是最小的(即T1是當John付款正好S時,最少需要多少張貨幣.
T2是當售貨員正好找錢S-m時,最少需要多少張貨幣.).
John給的錢肯定>=m, 但是到底最大多大呢? 如果我們直接求John的所有金錢總和, 然后再DP, 肯定超時. 這個up_bound (即john最多給售貨員的錢數) 可以簡單設置1個大數值便可. 網上有個證明(這個證明我也有點不明白):
John的付款數最多為maxv*maxv+m
證明以下:
如果John的付款數大于了maxv*maxv+m,即付硬幣的數目大于了maxv,根據鴿笼原理,最少有兩個的和對maxv取模的值相等(這個意思應當是:最少maxv+1個硬幣對maxv求余,然后余數屬于[0,maxv⑴]范圍,肯定有最少兩個硬幣的余數相同的),也就是說,這部份硬幣能夠用更少的maxv來代替(這句話我不理解)。證畢。
第1個問題是1個多重背包問題.
令dp[i][j]==x 表示當John用前i種貨幣組成j元錢時, 最少需要x張貨幣.
初始化: dp全為INF(無窮大), 且dp[0][0]=0.
對每種貨幣, 我們分情況對它進行處理:
1. 如果val[i]*num[i]>=up_bound時, 做1次完全背包.
2. 如果val[i]*num[i]<up_bound時, 做屢次01背包.
終究所求: dp[n][i] 其中i屬于[m, up_bound].
第2個問題是1個完全背包問題.
令dp2[i][j]==x 表示售貨員用前i種硬幣組成j元錢時, 最少需要x張貨幣.
初始化: dp2全為INF(無窮大), 且dp2[0][0]=0.
狀態轉移: dp2[i][j] = max( dp2[i⑴][j] , dp2[i][j-val[i]]+1 )
終究所求: dp2[n][i] 其中i屬于[m, up_bound].
終究合并問題1和問題2的解, 我們枚舉i從m到up_bound, 找出dp[i]+dp2[i-m]的最小值便可.
AC代碼:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 1e9
int n;//n種硬幣
int m;//購買商品的價值
int up_bound;//DP價值上界
int val[100+5];//每種硬幣價值
int num[100+5];//每種硬幣數目
int dp[55555]; //多重背包
int dp2[55555];//完全背包
//1次01背包
void ZERO_ONE_PACK(int *dp,int cost,int sum)
{
for(int i=up_bound;i>=cost;i--)
dp[i] = min(dp[i], dp[i-cost]+sum);//注意這里是+sum
}
//1次完全背包
void COMPLETE_PACK(int *dp,int cost)
{
for(int i=cost;i<=up_bound;i++)
dp[i] = min(dp[i], dp[i-cost]+1);
}
//1次多重背包
void MULTIPLY_PACK(int *dp,int cost,int sum)
{
if(cost*sum>=up_bound)
{
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()
{
while(scanf("%d%d",&n,&m)==2)
{
//讀取輸入+計算上界
int max_val=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&val[i]);
max_val= max(max_val,val[i]);
}
for(int i=1;i<=n;i++)
scanf("%d",&num[i]);
up_bound=max_val*max_val+m;//上界
//初始化dp和dp2
for(int i=1;i<=up_bound;i++)
dp[i]=dp2[i]=INF;
dp[0]=dp2[0]=0;
//遞推進程
for(int i=1;i<=n;i++)
{
MULTIPLY_PACK(dp,val[i],num[i]);
COMPLETE_PACK(dp2,val[i]);
}
//統計結果
int ans=INF;
for(int i=m;i<=up_bound;i++)
ans= min(ans, dp[i]+dp2[i-m]);
printf("%d
",ans==INF?⑴:ans);
}
return 0;
}
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈