多重部分和问题(动态规划(DP))

注:文章内容源自《挑战程序设计竞赛》(第二版)

原题

多重部分和问题

有n种不同大小的数字ai,每种各mi个。判断是否可以从这些数字之中选出若干使它们的和恰好为K。
1<=n<=100
1<=ai,mi<=100000
1<=K<=100000

样例输入


n=3
a={3,5,8}
m={3,2,2}
K=17

样例输出


Yes(3*3+8=17)

涉及知识及算法


定义dp[i+1][j]为前i种数字是否能加和成j
为了用前i种数字加和成j,也就需要能用前i-1种数字加和成j,j-ai,…,j-mi*ai中的某一种。由此我们可以定义如下递推关系:
dp[i+1][j]=(0<=k<=mi且k*ai<=j时存在使dp[i][j-k*ai]为真的k)

代码

//数列的长度
int n;
//目标的和数
int K;
//值
int a[MAX_N];
//个数
int m[MAX_N];
bool dp[MAX_N+1][MAX_K+1];

void solve()
{
    dp[0][0]=true;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<=K;j++)
        {
            for(int k=0;k<=m[i]&&k*a[i]<=j;k++)
            {
                dp[i+1][j]=dp[i][j-k*a[i]];
            }
        }
    }
    if(dp[n][K])
    {
        printf("Yes\n");
    }
    else
    {
        printf("No\n");
    }
}
这个算法并不够好,复杂度为O(K∑imi)。一般用DP求取bool结果会有不少浪费,同样的复杂度往往能获得更多信息。在这个问题中,我们不光求出能否得到目标的和数,同时把得到时ai这个数还剩下多少个可以使用 计算出来,这样就可以减少复杂度。
可以定义dp[i+1][j]为用前i种数加和得到j时第i种数最多能剩余多少个(不能加和得到i的情况下为-1)
这样如果前i-1个数加和能得到j的话,第i个数就可以留下mi个。此外,前i种数加和出j-ai时第i种数还剩下k(k>0)的话,用这i种数加和j时第i种数就能剩下k-1个。由此可得:
dp[i+1][j]={ mi (dp[i][j]>=0)
{ -1 (j<ai或者dp[i+1][j-ai]<=0)
{ dp[i+1][j-ai]-1 (其他)
这样只要看最终是否满足dp[n][K]>=0就可以知道答案了。
这个递推式可以在O(nK)时间内计算出结果。再将数组重复利用,则可以得到:
int dp[MAX_K+1];
void solve()
{
    memset(dp,-1,sizeof(dp));
    dp[0]=0;
    for(int i=0;i<n;i++)
    {
        for(int 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\n");
    }
    else 
    {
        printf("No\n");
    }
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值