[多重背包] POJ 1276 Cash Machine
来源:互联网 发布:js 创建class 编辑:程序博客网 时间:2024/06/01 07:29
一句话题意:
基本是多重背包裸题.
分析:
训练赛时的一道题,然而当时忘了多重背包是什么样的了…急中生智,发明了一种算法,感觉效率还是蛮高的.(之后在VJ上发现了一个和我一模一样思路的,也谈不上发明吧)
这是在完全背包上加了一个cnt数组,让完全背包无限放变成了有限个,cnt数组就是记录第i个物品用了几个.
下面先放上一个完全背包的代码:
for (int i = 1; i <= n; i++) for (int j = data[i].value; j <= cash; j++) if(dp[j - data[i].value]) dp[j] = true;
如上面代码所示,dp[j]代表使用前i种面额(无限使用)能不能到达j.
但此题每种货币有数量限制,那么就增加一个cnt数组限制第i种面额的使用次数.cnt[j]就表示在全部前i-1种面额组成的所有面额的基础上,再用几张第i中面额,能组成j.
比如在前i种循环中dp[j - v]标记为true,第i中面额为v,数量为k,只要满足:
dp[j - v]为true,(j - v的数量,再加上第i种面额v,就能到达j)
且cnt[j - v] < k(这种货币的使用次数在组成j - v后使用不超过k - 1次)
那么使用第[i]种面额就能组成j.
但是能组成j,此时不要先标记在dp数组中,cnt数组中有标记就可以了,其实不用刻意的去标记,使用了一次第i中货币组成了[j],cnt数组的j位置出就是一个大于1的数.
现在理一理思路:
前i - 1种货币组成的面额标记在dp数组中,
在前i - 1种的基础上新组成的面额标记的cnt数组中(可能会有重复)
每一种货币跑完以后,就把cnt数组中的标记转移到dp数组中就可以了
我们用一次新的面额,都清零一次cnt数组,cnt数组里存的是当前物品的使用次数.
for (int i = 0; i <= n - 1; i++) { memset(cnt, 0, sizeof(cnt));//cnt数组标记的是第i种货币的使用次数,每次循环都要清零 for (int j = data[i].value; j <= cash; j++) { if(dp[j - data[i].value] && data[i].num) { cnt[j] = 1;//在dp中发现j可以达到,那么此时肯定使用的是第一张 } else if(cnt[j - data[i].value] && cnt[j - data[i].value] < data[i].num) { cnt[j] = cnt[j - data[i].value] + 1;//在cnt数组中发现j可以达到,那么肯定使用不止一张 } } for (int j = 0; j <= cash; j++)//把cnt数组中的标记转移到dp数组中 if(cnt[j]) dp[j] = true; }
这样跑完第i遍后,dp数组内存的就是所有能组成的面额的情况了.
代码:
#include <iostream>#include <cstring>#include <algorithm>#include <string>#include <cstdio>using namespace std;bool dp[100010];int cnt[100010];struct bill{ int num, value;}data[100];int main(){ int cash, n; while(cin >> cash) { memset(dp, false, sizeof(dp)); dp[0] = true; cin >> n; for (int i = 0; i <= n - 1; i++) scanf("%d %d", &data[i].num, &data[i].value); for (int i = 0; i <= n - 1; i++) { memset(cnt, 0, sizeof(cnt));//cnt数组标记的是第i种货币的使用次数,每次循环都要清零 for (int j = data[i].value; j <= cash; j++) { if(dp[j - data[i].value] && data[i].num) { cnt[j] = 1;//在dp中发现j可以达到,那么此时肯定使用的是第一张 } else if(cnt[j - data[i].value] && cnt[j - data[i].value] < data[i].num) { cnt[j] = cnt[j - data[i].value] + 1;//在cnt数组中发现j可以达到,那么肯定使用不止一张 } } for (int j = 0; j <= cash; j++)//把cnt数组中的标记转移到dp数组中 if(cnt[j]) dp[j] = true; } for (int i = cash; i >= 0; i--) { if(dp[i]) { cout << i << endl; break; } } } return 0;}
阅读全文
0 0
- POJ 1276 Cash Machine(多重背包)
- poj 1276 Cash Machine(多重背包+倍增)
- poj 1276 Cash Machine(多重背包)
- POJ 1276 Cash Machine (多重背包)
- poj 1276 Cash Machine (多重背包)
- poj 1276 Cash Machine----多重背包
- POJ 1276 Cash Machine (多重背包)
- POJ 1276 Cash Machine 多重背包
- 多重背包 poj 1276 Cash Machine
- 【POJ】1276 Cash Machine(多重背包)
- 【DP|多重背包】POJ-1276 Cash Machine
- poj 1276 Cash Machine(多重背包)
- POJ 1276 Cash Machine【多重背包DP】
- poj 1276 Cash Machine (多重背包)
- POJ 1276 Cash Machine(多重背包)
- poj 1276 Cash Machine(多重背包)
- poj 1276 Cash Machine (多重背包)
- poj 1276 Cash Machine (多重背包)
- nyoj-119-士兵杀敌(二)【线段树】
- C++中lambda表达式的使用
- Centos6 ping 外网返回 connect: Network is unreachable
- HDU 6112 今夕何夕 (zeller\基姆拉尔森公式 星期公式)
- 「网络流 24 题」圆桌聚餐
- [多重背包] POJ 1276 Cash Machine
- DAY 3の继续分治(一个很气的错误)
- 开心的小明-动态规划算法
- 疯狂对列
- CSU 1803 2016
- ZOJ 2112 Dynamic Rankings (动态第k大,树状数组套主席树)
- 浅谈机器学习
- HDU-4315 Climbing the Hill(阶梯博弈)
- DPM(Deformable Parts Model)--原理(一)