• 多重部分和问题 (dp)


    题目描述
    有n种不同大小的数字Ai,每种各Mi个。判断是否能从这些数字中选出若干个使它们的和恰好为K。

    这个问题可以用DP求解,递推关系式的定义会影响最终的复杂度。
    第一种定义:
    dp[i+1][j],用前i种数字是否能加和成j

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

    #include<iostream>
    #include<stdio.h>
    using namespace std;
    int n,K;
    int a[100],m[100];///a表示数字大小,m表示这个数字的个数
    bool dp[100][100];///dp数组
    
    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])///dp[n][k]存在,即前n个数字能组成和K
            printf("Yes
    ");
        else
            printf("No
    ");
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        for(int i=0;i<n;i++)
            scanf("%d",&m[i]);
        scanf("%d",&K);
        solve();
        return 0;
    }
    

    但是这种方法的时间复杂度比较大,因为一般用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)
    dp[i+1][j]=-1; (j<Ai或者dp[i+1][j-Ai]<=0)
    dp[I+1][j]=dp[I+1][j-Ai]-1; (其他)
    这样,只要看最终结果是否满足dp[n][K]>=0就知道答案啦。

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    int n,K;
    int a[100],m[100];///a表示数字大小,m表示这个数字的个数
    bool dp[100];///dp数组
    
    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)///如果能组成数字j的话,
                    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
    ");
        else
            printf("No
    ");
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=0; i<n; i++)
            scanf("%d",&a[i]);
        for(int i=0; i<n; i++)
            scanf("%d",&m[i]);
        scanf("%d",&K);
        solve();
        return 0;
    }
    
  • 相关阅读:
    PetShop 4.0讨论专贴(Q&A)
    Google完成对Writely的整合
    Atlas学习手记(29):JavaScript面向对象的扩展(三):接口Interface
    Atlas学习手记(25):使用行为增强用户界面(五):AutoComplete Behavior
    Atlas学习手记(21):使用行为增强用户界面(一):Click Behavior
    Atlas学习手记系列
    Atlas学习手记(24):使用行为增强用户界面(四):Popup Behavior
    Atlas学习手记(27):JavaScript面向对象的扩展(一):命名空间Namespace
    同一个联盟,同一个梦想 —— 微软 .NET 俱乐部 2006 年在线发布会
    微软中文新闻组提供免费在线支持
  • 原文地址:https://www.cnblogs.com/cmmdc/p/7196225.html
Copyright © 2020-2023  润新知