基本动态规划题学习笔记与解析——6(多重部分和问题)


有n种不同大小的数字ai ,每种有mi 个,判断是否可以从这些数字中选出若干使他们的和恰好为k

样例输入
n=3
a i a_i ai m i m_i mi

3 3
5 2
8 2
k=17

输出
YES (3*3+8=17)

思路
同样是用基本的dp来求解,一般遇到这种问题,我们首先要找到他的递推式
我们假定dp【i】【j】表示 “用前 i 种数字加和得到j时还剩多少个m【i】,如果不能得到则取-1”
因此我们得到递推式

1.当dp【i-1】【j】>=0时,代表前i-1个数字已经可以加和成j ,第i个数字不用选也能成立 所以 dp【i】【j】=m【i】;
2.当 j<a【i】或者 dp【i】【j-a[i]】<0时,代表当前j不够选a【i】或者选了a【i】后并不能加和成j ,所以此时dp【i】【j】= -1;
3.最后只剩下可以选择 a【i】的情况,因此在这个情况下选择后 ,
dp【i】【j】=dp【i】【j-a【i】】-1 (对应于选择前对应a【i】数量-1)

找着这个对应关系很快就能写出对应代码。

而如果想要更加简化代码(非必要),我么可以将dp数组中的i省略降为一维数组
代码如下

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
int main()
{
	int dp[100];
	int n,i,t,j,k;
	int a[100],m[100];
	memset(dp,-1,sizeof(dp));
	dp[0]=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&a[i],&m[i]);
	}
	scanf("%d",&k);
	for(i=1;i<=n;i++)
	for(j=0;j<=k;j++)
	{
		if(dp[j]>=0)
		{
			dp[j]=m[i];
		}
		else if(j<a[i]||dp[j-a[i]]<0)
		{
			dp[j]=-1;
		}
		else
		{
			dp[j]=dp[j-a[i]]-1;
		}
	}
	if(dp[k]>=0)
	{
		printf("YES");
	}
}

为何省略i后与原来一样 ?
因为每一次在新的循环,dp【j】对应的数值是在i-1基础上处理后得到的数值
我们求出在i的基础上处理的数值后直接存储进dp【j】,而再之后i-1对应的数值不再会用到了,被取代也没有关系。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值