1. 程式人生 > >HDU 3591 The trouble of Xiaoqian(多重背包+全然背包)

HDU 3591 The trouble of Xiaoqian(多重背包+全然背包)

給他 cas 維數 color cost 代碼 01背包 size code

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(多重背包+全然背包)