POJ 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

题目大意:

简单的多重部分和问题,给你几个数字还有这几个数字的个数,问你能在
1-m这m个数字中能凑出多少来?

题目解析:

方法一(超出内存,但方法可行):

  1. 这种方法思路很清晰,很容易上手。
  2. 最外层循环是从0到n-1,一种数字一种数字分析,第二层循环是0-m,每一个位置都做讨论,第三层循环从0-c[i],就是对数字的个数进行一个一个判断。
  3. 需要准备dp[MAXN+1][MAXN+1]大小的数组(这也正是这道题超出内存的原因)初始化dp[0][0]为true,这个也好理解,前0种数字可以组成0,没毛病吧。
  4. 如果j-ka[i]大于等于0,那么我们就可以进行判断,为了代码简单化,我们直接使用dp[[i+1][j] |=dp[i][j-ka[i]],表示如果前面i种数字能组成j-k*a[i],那么再加上k个a[i]个值,这个值也就能凑成了,要是凑不成,那还是false。

代码:

方法一: 
		int res=0;
		dp[0][0]=true;
		for(int i=0; i<n; i++) {
			for(int j=0; j<=m; j++) {
				for(int k=0; k<=c[i]&&(j-k*a[i]>=0); k++) {
					dp[i+1][j] |=dp[i][j-k*a[i]];
				}
			}
		}
		for(int i=1;i<=m;i++){
			if(dp[n][i]){
				res++;
			}
		}

方法二(可以AC,代码简洁):

  1. 这种方法同样可以使用二维数组来实现,但是前面说过了,如果开这样一个二维数组,内存会超出限制,所以,我们就是用一维数组。
  2. dp[j]表示的含义就不仅仅是能不能组成j这个数字那么简单了,对于每一次循环,它表示的是当前第i种数字还剩下多少,如果能组成j数字,那自然剩下的数字大于等于0,要是组不成,那就是1。
  3. 需要分三种情况
    ①如果dp[j]大于等于0,那么说明我这个数字都不需要了,还能下c[i]个a[i]数字。
    ②如果j-a[i]小于0(j数字都没有a[i]大,更别说组成数字了)或者是dp[j-a[i]]小于等于0(表示前面j-a[i]要么是无法组成,要么是c[i]数字用完了,不能继续组成数字了),那么就让dp[j]=-1,意思是组不成前i种数字组不成这个j了。
    ③如果dp[j-a[i]]大于0,说明a[i]这个数字还是有剩余的,那么可以继续在j-a[i]的基础上再加个a[i]继续组成j,由于组成j又用了一个a[i],那么剩下的个数又少了一,所以dp[j]=dp[j-a[i]-1。

代码:

//方法二
		dp[0]=0;
		for(int i=0;i<n;i++){
			for(int j=0;j<=m;j++){
				if(dp[j]>=0){
					dp[j]=c[i];
				}
				else if(j-a[i]<0||dp[j-a[i]]<=0){
					dp[j]=-1;
				}else{
					dp[j]=dp[j-a[i]]-1;
				}
			}
		}
		for(int i=1;i<=m;i++){
			if(dp[i]>=0){
				res++;
			}
		}

如果能熟练掌握第二种方法最好了,因为它不仅占用空间少,逻辑也挺简单的,也能实现方法一的功能。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值