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

问题描述

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

限制条件

1 <= n <= 100

1 <= ai, mi, <= 100000

1 <= K <= 100000

方式一

定义bool  dp[i+1][j] 前i个数(含)能否加和为 j

for (int k = 0; k*a[i] <= j && k <= m[i]; k++)

dp[i+1][j] |= dp[i][j-k*a[i]];

这样程序的复杂度是O(K∑mi), 并且 bool dp 的信息有点浪费

方式二

定义dp[i+1][j] 前 i+1个数加和得到 j 时 第i个数能剩余多少个(没有时为-1)

dp[i+1][j] =
{
-1     (a[i] > j || dp[i+1][j-a[i]] <= 0) //大于j或者已经没有了

m[i]   (dp[i][j] >= 0) //前i-1个数已经到达j 那么不需要消耗第i个数

dp[i+1][j-a[i]] - 1 (其他)
}

这样复杂度O(nK)

输入:

3
3 5 8
3 2 2
17

输出:

Yes

code:

#include <iostream>

using namespace std;
const int MAX=100000;
int n,k;
int a[MAX],m[MAX];
bool dp[100][MAX];
void solve()
{
    int i,j,t;
    for(i=1; i<k; i++)
        dp[0][i]=false;
    for(i=0; i<=n; i++)
        dp[i][0]=true;
    int r,f;
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=k; j++)
            for(t=0; t<=m[i]&&t*a[i]<=j; t++)
                dp[i][j]|=dp[i-1][j-t*a[i]];
        for(r=1; r<=n; r++)
        {
            for(f=1; f<=k; f++)
                cout<<dp[r][f]<<" ";
            cout<<endl;
        }
    }
    if(dp[n][k])
        cout<<"Yes"<<endl;
    else
        cout<<"No"<<endl;
}
void solve1()
{
    int i,j,t;
    int d[MAX];
    for(i=1; i<=k; i++)
        d[i]=-1;
    d[0]=0;
    for(t=0; t<=k; t++)
        cout<<d[t]<<" ";
    cout<<endl;
    for(i=1; i<=n; i++)
    {
        for(j=0; j<=k; j++)
        {
            if(d[j]>=0)
                d[j]=m[i];
            else if(j<a[i]||d[j-a[i]]<=0)
                d[j]=-1;
            else
                d[j]=d[j-a[i]]-1;
        }

        for(t=0; t<=k; t++)
            cout<<d[t]<<" ";
        cout<<endl;
    }

    if(d[k]>=0)
        cout<<"Yes"<<endl;
    else
        cout<<"No"<<endl;
}
int main()
{
    while(cin>>n)
    {
        int i;
        for(i=1; i<=n; i++)
            cin>>a[i];
        for(i=1; i<=n; i++)
            cin>>m[i];
        cin>>k;
        solve();
        solve1();
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

为君倾此杯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值