多重部分和问题

题目描述:

  • n n n中大小不同的数字 a i a_i ai,每种数字 m i m_i mi,判断是否可以从这些数字之中选出若干使它们的和恰好为 K K K

限制条件:

  • 1 ≤ n ≤ 100 1≤n≤100 1n100
  • 1 ≤ a i , m i ≤ 100000 1≤a_i,m_i≤100000 1ai,mi100000
  • 1 ≤ K ≤ 100000 1≤K≤100000 1K100000

题解:

一般动态规划:

  • 步骤
    • dp数组含义 d p [ i ] [ j ] dp[i][j] dp[i][j]=用前 i i i种数字是否能拼成 j j j
    • 初始条件 d p [ 0 ] [ 1 − K ] = F a l s e ; d p [ 0 − n ] [ 0 ] = T u r e dp[0][1-K]=False; dp[0-n][0]=Ture dp[0][1K]=False;dp[0n][0]=Ture
    • 递推公式 d p [ i ] [ j ] = { ∃ d p [ i − 1 ] [ j − k × a i ] 为 真 ∣ 0 ≤ k ≤ m i & k × a i ≤ j } dp[i][j]=\{∃ dp[i-1][j-k×a_i ]为真|0≤k≤m_i \& k×a_i≤j\} dp[i][j]={dp[i1][jk×ai]0kmi&k×aij}
    • 递推方向:从上向下
    • 结果 d p [ n ] [ K ] dp[n][K] dp[n][K]是否为真
  • 时间复杂度 O ( K ∑ i m i ) O(K∑_im_i ) O(Kimi)
  • 代码 :函数 s o l v e 1 ( ) solve1() solve1()

优化动态规划:

  • 上面的动态规划的优化方向
    • 第一: dp数组中只存取bool型结果会有不少浪费,可以用来存取更有效的信息
    • 第二:我们递推只使用用了从上到下的递推方向,我们可能可以使用左到右的递推方向来辅助
  • 步骤
    • dp数组含义 d p [ i ] [ j ] dp[i][j] dp[i][j]=用前 i i i种数字是拼成 j j j,第i种数字最多剩余多少个,如果拼不成j就令其为-1
    • 初始条件 d p [ 0 ] [ 1 − K ] = − 1 ; d p [ 0 ] [ 0 ] = 0 , d p [ 1 − n ] [ 0 ] = m i dp[0][1-K]=-1; dp[0][0]=0,dp[1-n][0]=m_i dp[0][1K]=1;dp[0][0]=0,dp[1n][0]=mi
    • 递推公式 d p [ i ] [ j ] = { m i d p [ i − 1 ] [ j ] ≥ 0 − 1 j &lt; a i o r d p [ i ] [ j − a i ] ≤ 0 d p [ i ] [ j − a i ] − 1 其 他 dp[i][j]=\begin{cases} m_i &amp; dp[i-1][j]≥0 \\ -1 &amp;j&lt;a_i or dp[i][j-a_i ]≤0\\ dp[i][j-a_i ]-1 &amp;其他\\\end{cases} dp[i][j]=mi1dp[i][jai]1dp[i1][j]0j<aiordp[i][jai]0
    • 递推方向:从上向下,从左到右
    • 结果 d p [ n ] [ K ] dp[n][K] dp[n][K]是否为真
  • 时间复杂度 O ( n K ) O(nK ) O(nK)
  • 代码 :函数 s o l v e 2 ( ) solve2() solve2()

代码:

#include <iostream>
#define Max_N   105
#define Max_K   100005
using namespace std;

int n,K;
int a[Max_N],m[Max_N];
bool dp[Max_N][Max_K];
int Dp[Max_N][Max_K];
int DP[Max_K];

//基本动态规划
void solve1()
{
    //初始化
    for(int i=1; i<=K; i++)
        dp[0][i]=false;
    for(int i=0; i<=n; i++)
        dp[i][0]=true;
    //递推
    for(int i=1; i<=n; i++)
        for(int j=1; j<=K; j++)
            for(int k=0; k<=m[i]&&k*a[i]<=j; k++)
                dp[i][j]|=dp[i-1][j-k*a[i]];
    //结果
    if(dp[n][K])
        cout<<"Yes"<<endl;
    else
        cout<<"NO"<<endl;
}
//优化动态规划
void solve2()
{
    //初始化
    for(int i=1; i<=K; i++)
        Dp[0][i]=-1;
    for(int i=1; i<=n; i++)
        Dp[i][0]=m[i];
    Dp[0][0]=0;
    //递推
    for(int i=1; i<=n; i++)
        for(int j=1; j<=K; j++)
        {
            if(Dp[i-1][j]>=0)
                Dp[i][j]=m[i];
            else if(j<a[i]||Dp[i][j-a[i]]<=0)
                Dp[i][j]=-1;
            else
                Dp[i][j]=Dp[i][j-a[i]]-1;
        }

    if(Dp[n][K]>=0)
        cout<<"YES"<<endl;
    else
        cout<<"NO"<<endl;
}

//内存优化,数组从用
void solve3()
{
    for(int i=1; i<=K; i++)
        DP[i]=-1;
    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(j<a[i]||DP[j-a[i]]<=0)
                DP[j]=-1;
            else
                DP[j]=DP[j-a[i]]-1;
        }
    if(DP[K]>=0)
        cout<<"YES"<<endl;
    else
        cout<<"NO"<<endl;
}

int main()
{
    cin>>n>>K;
    for(int i=1; i<=n; i++)
        cin>>a[i]>>m[i];
    solve1();
    solve2();
    solve3();
    return 0;
}
/*
3 17
3 3
5 2
8 2
*/
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值