poj1276 cash machine

这道题是一个多重背包问题。

以前只是用二维数组,带记忆的递归做过01背包问题,做这个多重背包问题很费劲。

要想明白,一维数组如何能记录最优解,要想明白如何把多重背包问题转化为01问题。直接把不同个数的同一种物品看作一种物品的方法,会使得物品太多,解的效率很低。

网上有很多效率高的方法,一种是把多重背包,按二进制数分成不同的物品,从而减少物品个数。也就是把n个相同的物品,转换为log(n)个 不同的物品。

另一种方法就是如下这种方法(借鉴别人的。刚开始没看懂,很来在纸上画了很多遍,懂了)。

归根结底还是要理解

for i=1..N //01背包
    for v=V..0  //注意v的变化顺序的不同哦  递减    //因为递减 就限制了每个物品只用一次
        f[v]=max{f[v],f[v-c[i]]+w[i]};

for i=1..N  //完全背包
    for v=0..V //递增                 //因为递增,所以每个物品可以随便用很多次
        f[v]=max{f[v],f[v-cost]+weight}

for i=1..N  //多重背包
   count[]=0;//都置为0
   for v=0..V //递增
   if(count[v-cost]<num[i]) //个数限制    //递增 但是限制物品用的次数
    f[v]=max{f[v],f[v-cost]+weight}

只要真正理解了这3*三行代码,背包问题就都是这样的了。其实。

01背包就是对个数都限制为1,多重背包就是要对个数加个不确定的限制。完全背包就是完全完全不加限制,价值能增加就随你的便增加。

当多重背包的每个物品个数都为1时,多重背包就是01背包。

也就是说下面两段代码是等价的:

for i=1..N 
    for v=V..0  //注意v的变化顺序的不同哦  递减
        f[v]=max{f[v],f[v-c[i]]+w[i]};
count[]={0}都置为0
for i=1..N  
    for v=0..V   //递增
   if(count[v-cost]<1) //个数限制
    f[v]=max{f[v],f[v-cost]+weight;
    count[v]=1;
}
想明白为什么等价的话,所有的背包就不是难题了。


#include<iostream> #include<fstream> #include<string.h> using namespace std; int main() { freopen("input.txt","r",stdin); int cash; while(scanf("%d",&cash)!=EOF) { int n; scanf("%d",&n); if(n==0) { printf("0\n"); continue; } int *bills=new int[n]; int *money=new int[n]; for(int i=0;i<n;i++) { scanf("%d %d",&bills[i],&money[i]); } if(cash==0) { printf("0\n"); delete [] bills; delete [] money; continue; } int *output=new int [cash+1]; int *count=new int[cash+1]; memset(output,0,sizeof(int)*(cash+1)); for(int i=0;i<n;i++) { memset(count,0,sizeof(int)*(cash+1)); for(int c=money[i];c<=cash;c++) { if(count[c-money[i]]<bills[i]&&output[c]<output[c-money[i]]+money[i]) { output[c]=output[c-money[i]]+money[i]; count[c]=count[c-money[i]]+1; } } } printf("%d\n",output[cash]); delete []bills; delete []money; delete []output; delete []count; } }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值