1. 程式人生 > >HDOJ 1114 Piggy-Bank

HDOJ 1114 Piggy-Bank

題目大意:已知存錢罐豬的重量和存錢罐豬以及罐內硬幣的總重量,已知每種硬幣的面值與重量,每種硬幣數量無限,求罐內硬幣的最小面值。

輸入。T:總的案例個數,針對每個案例有E,F:罐的重量以及罐+硬幣的總重量。N:硬幣的種類數,之後有N行,每行為P:硬幣的面值,以及W:硬幣的重量。

這個題目要求硬幣的總重量應該恰好等於F-E,如果根據給定的硬幣資料,硬幣的重量無法恰好等於F-E,則輸出Impossible。如果硬幣重量可以恰好等於F-E,則輸出可能的硬幣最小的價值。

問題為完全揹包的問題,F-E可以看做揹包的重量,硬幣可以看做物品。設f[i](j)為硬幣總重量為i時,從前j種硬幣選擇,最小的面值。則此時有兩種選擇:1、不選擇第j種硬幣,則f[i](j)=f[i](j-1)。2、選擇一個第j種硬幣,則擁有了P[j]的價值,硬幣總重量變為了i-W[j],由於硬幣數量無限,則接下來還是從前j種硬幣選擇,獲取硬幣的最小面額為f[i-W[j] ] + P[j]。則此時f[i] (j) = min { f[i](j-1) , f[i-W[j] ] + P [j] }。

如果用一維陣列求解,則根據完全揹包的講解,在兩層for迴圈中(硬幣總重量的for迴圈i以及從前j種硬幣選擇的for迴圈j)需要全部正序遍歷。我在看到完全揹包講解的時候並沒有理解為什麼都要正序(0-1揹包中是逆序),也是通過一個具體的問題,手動運行了虛擬碼以後理解得更加深刻一些。則我們求解的虛擬碼可以表示為:

for(int i=1;i<=weight;i++){
            for(int j=1;j<=N;j++){
                if(i-W[j]>=0){
                    f[i]=min(f[i],f[i-W[j]]+P[j]);
                }
            }
        }

設硬幣的總重量(F-E)為weight,則我們的最終目標就是求解f[weight]。

這裡還有一個問題,就是f的初值定位多少的問題。我的求解思路是這樣的,由於需要求解面值的最小值,那麼一開始我就把f定為一個無窮大,這個時候當我求解出一個更小的解時,就進行替換,一直到最後把最小的值進行替換。那麼,題目中的正無窮究竟是多少呢?題目中要求1<=E<=F<=10000,1<=P<=50000,1<=W<=10000,那麼極端的情況下,就是硬幣總重量為10000(近似),有一個重量為1的硬幣,面值為50000,此時最大的面值為10000*50000,我們求解出的任何一個面值都會比它小,那麼我們就可以將f初始化為這個值。

注意f[0]需要初始化為0,因為可能存在這樣的情況:硬幣總重量為1,有一種重量為1,面值為2的硬幣。那麼,此時f[1]=min ( f[1], f[1-1]+P[1])求解出來為2。這些都確定下來,就可以寫出整個題目的程式了:

#include <iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int f[10005];//最小面值
int P[502],W[502];
const int max_value=10000*50000;

void fInit(int *f);
int main(){
    int T;//cases總數
    int E,F;//豬的重量,總重量
    int N;//硬幣種數
    int weight;//硬幣的總重量

    cin>>T;
    for(int i=1;i<=T;i++){
        fInit(f);
        cin>>E>>F;
        cin>>N;
        for(int j=1;j<=N;j++){
            cin>>P[j]>>W[j];
        }
        weight=F-E;

        for(int k1=1;k1<=weight;k1++){
            for(int k2=1;k2<=N;k2++){
                //硬幣重量k1,
                if(k1-W[k2]>=0){
                    f[k1]=min(f[k1],f[k1-W[k2]]+P[k2]);
                }
            }
        }
        if(f[weight]==max_value){
            cout<<"This is impossible."<<endl;
        }else{
            cout<<"The minimum amount of money in the piggy-bank is "<<f[weight]<<"."<<endl;
        }

    }


}

void fInit(int * f){
    for(int i=1;i<10005;i++) f[i]=max_value;
    f[0]=0;
}