POJ 1742 Coins 动态规划 多重部分和问题 解题模板

Description
People in Silverland use coins.They have coins of value A1,A2,A3…An Silverland dollar.One day Tony opened his money-box and found there were some coins.He decided to buy a very nice watch in a nearby shop. He wanted to pay the exact price(without change) and he known the price would not more than m.But he didn’t know the exact price of the watch.
You are to write a program which reads n,m,A1,A2,A3…An and C1,C2,C3…Cn corresponding to the number of Tony’s coins of value A1,A2,A3…An then calculate how many prices(form 1 to m) Tony can pay use these coins.
Input
The input contains several test cases. The first line of each test case contains two integers n(1<=n<=100),m(m<=100000).The second line contains 2n integers, denoting A1,A2,A3…An,C1,C2,C3…Cn (1<=Ai<=100000,1<=Ci<=1000). The last test case is followed by two zeros.
Output
For each test case output the answer on a single line.
Sample Input
3 10
1 2 4 2 1 1
2 5
1 4 2 1
0 0
Sample Output
8
4

题目大意:
用n种不同面额的硬币去购买玩具,玩具的价格在区间[1, m]中,在正好支付相应价格的情况下,求出价格可以取得的个数。

解题思路:
可归结为多重部分和问题,即有n种不同大小的数字ai,每种mi个。判断是否可以从这些数字中选出若干使得它们的和恰好为某一值。

解题模板:

/*dp[k]表示得到对应数值(即k)时,当前硬币的使用个数
每当考虑另一类硬币时,需要重新初始化
目的是将数组重复利用,不用使用二维数组*/
//bool数组can_buy[k]表示该数值(即k)能否被组成
for (int i = 1; i <= n; i++) {//硬币种类  i代表考虑第i种硬币
	for (int k = 0; k <= m; k++) dp[k] = 0;//每当考虑另一类硬币时,需要重新初始化
	for (int j = a[i]; j <= m; j++) {
		//如果数值j已经能被前i-1种硬币组成,则跳过
		//加上一枚种类i的硬币的前提条件是数值j-a[i] 能够被组成
		//如果为了得到该前提条件已经耗光了硬币i的数量,也跳过
		if (!can_buy[j] && can_buy[j - a[i]] && dp[j - a[i]] < c[i]) {
			can_buy[j] = true;
			dp[j] = dp[j - a[i]] + 1;
		}
	}
}

AC代码

#include<iostream>
#include <stdio.h>
using namespace std;

int a[101]; int c[101];
int dp[100001]; bool can_buy[100001];

int main() {
	int n, m;
	while (scanf("%d%d",&n,&m) != EOF) {
		if (n == 0 && m == 0) break;
		for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); }
		for (int i = 1; i <= n; i++) { scanf("%d", &c[i]); }
		for (int i = 1; i <= m; i++) can_buy[i] = false;
		can_buy[0] = true;
		for (int i = 1; i <= n; i++) {//硬币种类
			for (int k = 0; k <= m; k++) dp[k] = 0;//表示得到对应数值时,当前硬币的使用个数 //每当考虑另一类硬币时,需要重新初始化
			for (int j = a[i]; j <= m; j++) {
				if (!can_buy[j] && can_buy[j - a[i]] && dp[j - a[i]] < c[i]) {
					can_buy[j] = true;
					dp[j] = dp[j - a[i]] + 1;
				}
			}
		}
		int ans = 0;
		for (int i = 1; i <= m; i++) {
			if (can_buy[i]) ans++;
		}
		cout << ans << endl;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值