多重背包(dp专题)

Posted caijiaming

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多重背包(dp专题)相关的知识,希望对你有一定的参考价值。

题目大意:输入n,代表有n种数,接下来n个数代表n种数,再接下来n个数代表每种数有多少个,在输入K,代表用这些数要加成的和

问你是否能加为K,能输出yes,不能输出no

这是一个典型的多重背包问题,可以用dp来求解,。但是如何定义递推关系会影响到最终的复杂度,首先我们先看一下如下定义:

dp[i+1][j];=用前i种数能否加成和为j

为了用前i种数加成j,也就需要能用前i-1种数字加成j,j-a[i],···,j-mi*a[i],中的某一种,由此我们可以定义如下递推关系

dp[i+1][j]=(0<=k<=mi,且k*a[i]<=j时,存在使dp[i][j-k*a[i]]为真的k;

看代码

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<cmath>
#include<math.h>
#include<algorithm>
#include<set>
#include<queue>
typedef long long ll;
using namespace std;
const ll mod=1e9+7;
#define INF 0x3f3f3f
bool dp[110][100050];
int main()
{
    memset(dp,false,sizeof(dp));
    dp[0][0]=true;//赋初值
    int n,s;
    int a[100050],b[100050];
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    for(int i=0;i<n;i++)
        cin>>b[i];
    cin>>s;
   //for(int i=0;i<n;i++)
     // cout<<a[i]<< <<b[i]<<endl;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<=s;j++)
        {
            for(int k=0;k<=b[i]&&k*a[i]<=j;k++)
            {
                dp[i+1][j]|=dp[i][j-k*a[i]];//注意这里的或运算,代表有一个为真则为真
            }
        }
    }
   //for(int i=0;i<=s;i++)
     //   cout<<dp[n][i]<< ;
    if(dp[n][s])
        cout<<"yes"<<endl;
    else
        cout<<"no"<<endl;
    return 0;
}

 上面这个算法的复杂度是比较大,并不够好。一般用dp求取bool 结果的话会有不少浪费,同样的复杂度通常能获得更多的信息

在这个问题中,我们不光求出能否得到目标的和数,同时把得到时a[i]这个数还剩多少个计算出来,这样就可以减少复杂度

dp[i+][j]:=用前i种数加和得到j时,第i种数还剩多少个(不能加的情况为-1)

按照如上所述的递推关系,这样如果前i-1个数加能得到j的话,第i个数就可以留下b[i]个了,此外,前i种数加和出j-a[i]时第i种数还剩k(k>0)个的话

,用这i种数加和为j时就能剩k-1个了,由此我们可以得出如下递推式:

dp[i+1][j]=b[i]     (dp[i][j]>=0)

dp[i+1][j]=-1       (j<a[i]||dp[i_1][j-a[i]]<=0)

dp[i+1][j]=dp[i+1][j-a[i]]-1     (其它)

看代码

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<cmath>
#include<math.h>
#include<algorithm>
#include<set>
#include<queue>
typedef long long ll;
using namespace std;
const ll mod=1e9+7;
#define INF 0x3f3f3f
int dp[110][100050];
int main()
{
    memset(dp,-1,sizeof(dp));
    dp[0][0]=0;//赋初值
    int n,s;
    int a[100050],b[100050];
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    for(int i=0;i<n;i++)
        cin>>b[i];
    cin>>s;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<=s;j++)
        {
            if(dp[i][j]>=0)
                dp[i+1][j]=b[i];
            else if(j<a[i]||dp[i+1][j-a[i]]<=0)
                dp[i+1][j]=-1;
            else
                dp[i+1][j]=dp[i+1][j-a[i]]-1;

        }
    }
    if(dp[n][s]>=0)
        cout<<"yes"<<endl;
    else
        cout<<"no"<<endl;
    return 0;
}

 

以上是关于多重背包(dp专题)的主要内容,如果未能解决你的问题,请参考以下文章

背包专题G - Dividing hdu 1059多重背包

背包专题 D - Coins hdu2844多重背包

HDU 2844 Coins (多重背包问题DP)

背包专题C - The trouble of Xiaoqian hdu3591混合背包:多重背包+完全背包

背包dp(多重)

背包问题