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 
#include
#include
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."<

 

你可能感兴趣的:(algorithm)