动态规划之多重部分和问题

动态规划问题,我们这里定义dp[i][j]为bool类型,其意义表示为用前i种数(下标为0~i-1)能否凑成和j,能的话则dp[i][j]=true,反之则dp[i][j]=false,那么根据其前一种状态dp[i-1]j-k*a[i-1],如果dp[i-1][j-k*a[i-1]]=true,那么对于第i个数a[i-1]我们只要再用k个,那么就可以用前i个数凑成和为j。这是很容易理解的。递推关系式为:
动态规划之多重部分和问题

代码如下:

#include<iostream>
using namespace std;
#define Max_N 100
#define Max_K 100000
bool dp[Max_N+1][Max_K+1];
int a[Max_N];
int m[Max_N];
int n,K;
 
void solve()
{
	dp[0][0]=true;
	for(int j=1;j<=K;j++)
	  dp[0][j]=false;          //用前0种数凑成和不为0的,肯定不行
	  
  //dp[i][j] 用前i种数(下标0~i-1)能不能凑成和为j 
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<=K;j++)
		{
			//下标为i-1的数使用k次 
			for(int k=0;k<=m[i-1] && k*a[i-1]<=j;k++)
			{
				//不断或等 
				dp[i][j] |= dp[i-1][j-k*a[i-1]];
			}
		}
	} 
	if(dp[n][K]) cout<<"Yes\n";
	else cout<<"No\n";
	return ;
}
int main()
{
	cin>>n>>K;
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
	}
	
	for(int i=0;i<n;i++)
	{
		cin>>m[i];
	}
	solve();
}

其实大家也能看到这个程序的缺点就在于三重循环下,效率还是比较低,那么我们就想着有没有更高效的方法,那么我们就可以使用另一种定义方法dp[i][j]表示:用前i种数(下标0~i-1)在能凑成和为j时,第i个数(下标为i-1)最多可以剩余多少个,-1表示前i种数凑不成和为j,递推关系式如下(每段的意义我将会写在程序的注释中):
动态规划之多重部分和问题

#include<string.h>
#include<algorithm>
#include<iostream>
#define maxn 100000
using namespace std;
int n,k;
int a[maxn+5],m[maxn+5];
int dp[105][maxn+5];
void solve()
{
    fill(dp[0],dp[0]+maxn+5,-1);  //前0种和凑成和不为0的,肯定不行
    dp[0][0]=0;
    /*dp[i][j] 用前i种数(下标0~i-1)在能凑成和为j时,第i个数(下标为i-1)
    最多可以剩余多少个,-1表示前i种数凑不成和为j */
    for(int i=1;i<=n;i++){
        for(int j=0;j<=k;j++){
                
            //如果前i-1个数能和成j则第i个数a[i-1]剩余为m[i-1]个
            if(dp[i-1][j]>=0)dp[i][j]=m[i-1];
            
            //前i-1种数凑不成和为j,那么就用第i个数a[i-1]尝试凑
            //如果要凑成的和j<a[i-1],肯定是凑不成j的
            //如果在用前i个数能凑成和j-a[i-1],但是这时候a[i-1]数用完了,那么也是凑不成的
            else if(j<a[i-1]||dp[i][j-a[i-1]]<=0)dp[i][j]=-1;
            
            //在前i种数能凑成和j-a[i-1]并且数a[i-1]还有剩余,那么是能凑成的,再用掉一个a[i-1],所以个数减一 
            else dp[i][j]=dp[i][j-a[i-1]]-1;
        }
    }
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++)
        scanf("%d",&a[i]);
    for(int i=0;i<n;i++)
        scanf("%d",&m[i]);
        solve();
    if(dp[n][k]>=0)printf("Yes\n");
    else printf("No\n");
    return 0;
}

相关文章:

  • 2022-12-23
  • 2021-05-31
猜你喜欢
  • 2021-07-15
  • 2022-12-23
  • 2022-03-03
  • 2022-12-23
  • 2021-10-28
  • 2021-07-10
  • 2021-12-15
相关资源
相似解决方案