多重背包(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;
}

原文地址:https://www.cnblogs.com/caijiaming/p/9311870.html

时间: 2024-11-05 14:25:21

多重背包(dp专题)的相关文章

POJ 1742 Coins 【多重背包DP】

题意:有n种面额的硬币.面额.个数分别为A_i.C_i,求最多能搭配出几种不超过m的金额? 思路:dp[j]就是总数为j的价值是否已经有了这种方法,如果现在没有,那么我们就一个个硬币去尝试直到有,这种价值方法有了的话,那么就是总方法数加1.多重背包可行性问题 传统多重背包三重循环会超时,因为只考虑是否可行,没有考虑剩余面额数量的因素. o(n*v)方法 #include <iostream> #include <cstdio> #include <string.h> #

【bzoj1531】[POI2005]Bank notes 多重背包dp

题目描述 Byteotian Bit Bank (BBB) 拥有一套先进的货币系统,这个系统一共有n种面值的硬币,面值分别为b1, b2,..., bn. 但是每种硬币有数量限制,现在我们想要凑出面值k求最少要用多少个硬币. 输入 第一行一个数 n, 1 <= n <= 200. 接下来一行 n 个整数b1, b2,..., bn, 1 <= b1 < b2 < ... < b n <= 20 000, 第三行 n 个整数c1, c2,..., cn, 1 <

DZY Loves Math II:多重背包dp+组合数

Description Input 第一行,两个正整数 S 和 q,q 表示询问数量.接下来 q 行,每行一个正整数 n. Output 输出共 q 行,分别为每个询问的答案. Sample Input 30 3 9 29 1000000000000000000 Sample Output 0 9 450000036 Hint 感谢the Loser协助更正数据对于100%的数据,2<=S<=2e6??,1<=n<=101810^{18}10?18??,1<=q<=10

背包DP专题

P1510 精卫填海 很容易看出这是个01背包,可是怎么转化模型呢?注意到,输出impossible的情况就是当体力值C不可以处理体积为V的石头的时候,很自然的想到:把体力值看作背包的体积,把石头的体积看作背包的权值.求最大剩余体力值->在背包体积尽可能小的情况下,背包的权值>=石头的体积V rd(V),rd(n),rd(C); rep(i,1,n){ rd(w[i]),rd(v[i]); } rep(i,1,n) dwn(j,C,v[i]) f[j]=max(f[j],f[j-v[i]]+w

poj 3260 The Fewest Coins 多重背包+完全背包

题目链接:http://poj.org/problem?id=3260 给店家的钱是多重背包 dp[] 店家的找钱是完全背包 dp2[] 然后最后 其中i表示多给了多少钱 也就是需要找回多少钱 int ans = INF; ans = min(ans, dp[m+i] + dp2[i]); 是一个比较简单的思路 神坑题 看到每种货币的面值不大于120 我就觉得找钱一定不会超过119块钱结果wa到死 后来才发现不是的啊 之所以我有这种思维定式是因为现实生活中有1块钱这种货币单位 考虑一种极限的情况

dp背包问题/01背包,完全背包,多重背包,/coin change算法求花硬币的种类数

一步一步循序渐进. Coin Change 具体思想:给你 N元,然后你有几种零钱S={S1,S2...,Sm} (每种零钱数量不限). 问:凑成N有多少种组合方式  即N=x1 * S1+x2*S2+...+xk*Sk (xk>=0,k=1,2..m) 设有f(x)中组合方式 有两种解答(自底向上回溯): 1.不用第m种货币   f(N,m-1) 2.用第m种货币 f(N-Sm,m) 总的组合方式为f(N,m)=f(N,m-1)+f(N-Sm,m) anything is nonsense,s

poj1276——dp,多重背包

poj1276——dp,多重背包 Cash Machine Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 28826   Accepted: 10310 Description A Bank plans to install a machine for cash withdrawal. The machine is able to deliver appropriate @ bills for a requested c

poj1014 dp 多重背包

1 //Accepted 624 KB 16 ms 2 //dp 背包 多重背包 3 #include <cstdio> 4 #include <cstring> 5 #include <iostream> 6 using namespace std; 7 const int imax_n = 120005; 8 int f[imax_n]; 9 int amount[7]; 10 int v; 11 int n=6; 12 int max(int a,int b) 1

POJ 1014 Dividing 【DP 之 多重背包 / 二进制优化】

Language: Default Dividing Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 63647   Accepted: 16488 Description Marsha and Bill own a collection of marbles. They want to split the collection among themselves so that both receive an equal