多重部分和问题(dp 2015年安徽省赛)

本文介绍了一个经典的多重背包问题,通过动态规划的方法解决如何从不同数量的数字中选择若干个使其和为特定值K的问题,并提供了两种不同的实现方式。
多重部分和问题
Time Limit: 10000 ms   Memory Limit: 256 MB
Total Submission: 40   Submission Accepted: 17
Description
有n种不同大小的数字,每种各个。判断是否可以从这些数字之中选出若干使它们的和恰好为K。

Input
首先是一个正整数T(1<=T<=100)
接下来是T组数据
每组数据第一行是一个正整数n(1<=n<=100),表示有n种不同大小的数字
第二行是n个不同大小的正整数ai(1<=ai<=100000)
第三行是n个正整数mi(1<=mi<=100000),表示每种数字有mi个
第四行是一个正整数K(1<=K<=100000)

Output
对于每组数据,如果能从这些数字中选出若干使它们的和恰好为K,则输出“Yes”,否则输出“No”,每个输出单独占一行

Sample Input
Original Transformed
2
3
3 5 8
3 2 2
17
2
1 2
1 1
4

Sample Output
Original Transformed
Yes
No

Source

安徽省2015年“京胜杯”大学生程序设计竞赛


题目大意:中文题目。

题目分析:就是一个多重背包的问题,然后做这题得到的一个感觉就是,做dp题,就是应该用dp的思路去求解。最外层循环数字的种类,内层循环数字。dp【i+1】【j】代表 组成数字j后第i种数字还剩下的个数。然后就有以下几种情况:

   一:   前i-1种数字已经可以组成数字j了,所以此时第i种数字还剩下m【i】个,没有使用。

二:  前i种数字组成j-a【i】已经不剩余了,或者a【i】大于j

三:其他情况。


代码一:这是用二维数组的

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 100005;
int a[maxn],m[maxn];
int dp[100][100005];
int main(){
	int t;
	cin>>t;
	while(t--){
		memset(dp,-1,sizeof(dp));
		int n,k;
		cin>>n;
		for(int i=1;i<=n;i++)
		   cin>>a[i];
		for(int i=1;i<=n;i++)
		   cin>>m[i];
		cin>>k;
		dp[1][0]=0;
		for(int i=1;i<=n;i++){
			for(int j=0;j<=k;j++){
				if(dp[i][j] >= 0)
				   dp[i+1][j] = m[i];
				else if(dp[i+1][j-a[i]] <= 0 || j < a[i] )
				   dp[i+1][j] = -1;
				else dp[i+1][j] = dp[i+1][j-a[i]] - 1;
			}
		} 
		if(dp[n+1][k]>=0)
		   cout<<"Yes"<<endl;
		else cout<<"No"<<endl;
	}
	return 0; 
} 

代码二:这是用一维数组的

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 100005;
int a[maxn],m[maxn];
int dp[100005];
int main(){
    int t;
    cin>>t;
    while(t--){
        memset(dp,-1,sizeof(dp));
        int n,k;
        cin>>n;
        for(int i=1;i<=n;i++)
           cin>>a[i];
        for(int i=1;i<=n;i++)
           cin>>m[i];
        cin>>k;
        dp[0]=0;
        for(int i=1;i<=n;i++){
            for(int j=0;j<=k;j++){
                if(dp[j] >= 0)
                   dp[j] = m[i];
                else if(dp[j-a[i]] <= 0 || j < a[i] )
                   dp[j] = -1;
                else dp[j] = dp[j-a[i]] - 1;
            }
        } 
        if(dp[k]>=0)
           cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    return 0; 
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值