九背包问题

​ 九背包问题

https://www.kancloud.cn/kancloud/pack/70125

https://www.cnblogs.com/Yeasio-Nein/p/bagpack.html


01背包题目


有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。


基本算法

这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。

用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是:
f [ i ] [ v ] = m a x f [ i − 1 ] [ v ] , f [ i − 1 ] [ v − c [ i ] ] + w [ i ] f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]} f[i][v]=maxf[i1][v],f[i1][vc[i]]+w[i]

模板

//无优化模板
for(int i=1;i<=n;i++)
{
    for(int c=0;c<=m;c++)
    {
        f[i][c]=f[i-1][c];
        if(c>=w[i])
        f[i][c]=max(f[i][c],f[i-1][c-w[i]]+v[i]);
    }
}
//一维数组优化
for(int i=1;i<=n;i++)
{
  for(int c=m;c>=0;c--)
  {
    if(c>=w[i])
     f[c]=max(f[c],f[c-w[i]]+v[i]);
  }
}
//常数优化
for(int i=1;i<=n;i++)
{
       sumw+=w[i];
       bound=max(m-sumw,w[i]);
       for(int c=m;c>=bound;c--)
      {
        if(c>=w[i])
        f[c]=max(f[c],f[c-w[i]]+v[i]);
      }
}

01背包例题;典型的01背包

hdoj2602:Bone Collector

#include<iostream>
#include<cmath>
#incldue<algorithm>
#include<queue>
#include<cstring>
#define ll long long int 
#define maxn 1005
using namespace std;
int v[maxn],w[maxn],dp[maxn];
int main()
{
 int t,n,V,i,j,ans;
 scanf("%d",&t);
 while(t--)
{
  memset(dp,0,sizeof(dp));
  scanf("%d %d",&n,&v);
  for(i=0;i<n;i++)
    scanf("%d",&v[i]);
  for(i=0;i<n;i++)
    scanf("%d",&w[i]);
   for(i=0;i<n;i++)
     for(j=V;j>=w[i];j--)
       dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    printf("%d\n",dp[v]);
}
 return 0;
}

完全背包题目


有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。


基本思路


这个问题非常类似于01背包问题,所不同的是每种物品有无限件。也就是从每种物品的角度考虑,与它相关的策略已并非取或不取两种,而是有取0件、取1件、取2件……等很多种。如果仍然按照解01背包时的思路,令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值。仍然可以按照每种物品不同的策略写出状态转移方程,像这样:
f [ i ] [ v ] = m a x f [ i − 1 ] [ v − k ∗ c [ i ] ] + k ∗ w [ i ] ∣ 0 < = k ∗ c [ j ] < = v f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[j]<=v} f[i][v]=maxf[i1][vkc[i]]+kw[i]0<=kc[j]<=v
或者


f [ i ] [ v ] = m a x f [ i − 1 ] [ v ] , f [ i ] [ v − c [ i ] ] + w [ i ] f[i][v]=max{f[i-1][v],f[i][v-c[i]]+w[i]} f[i][v]=maxf[i1][v],f[i][vc[i]]+w[i]

//完全背包
for(int i=1;i<=n;i++)
{
    for(int c=0;c<=m;c++)
    {
        if(c>=w[i])
        f[c]=max(f[c],f[c-w[i]]+v[i]);
    }
}

完全背包例题

1. HDU- 1114 - Piggy-Bank

题意: 一个存钱罐,给你一开始体积和总共可以装的体积,然后在给你n 个硬币,给你相应的价值和体积,问你装满的话最少可以装多少钱,硬币可以用无限次
分析: 一个完全背包的板子题,直接看代码。

#include<bits/stdc++.h>
using namespace std;
const int N=le5+10,INF=0x3f3f3f3f;
int  V[N],W[N],dp[N],s,e;
 ios_base::sync_with_stdio(0);
int T;
cin>>T;
while(T--)
{
    int n;
    cin>>s>>e>>n;
    for(int i=0;i<N;i++)
        dp[i]=INF;
    dp[0]=0;
    for(int i=0;i<n;i++)
    {
        cin>>V[i]>>W[i];
    }
    for(int i=0;i<n;i++)
    {
        for(int j=w[i];j<=e-s;j++){
            dp[j]=min(dp[j],dp[j-w[i]+v[i]]);
        }
        if(dp[e-s]==INF) puts("This is imposssible");
        else
          printf("The minimum amount of money in the piggy-bank is%d",dp[e-s]);
    }
    return 0;
}

HDU - 1248 - 寒冰王座

题意:只给你一个N元的钞票,让你求你最少浪费多少钱,你可以买三样东西价格分别为150,200,350,每个可以买无限次
分析:这个也是比较正的板子题,直接看代码

#include<bits/stdc++.h>
using namespace std;
const int N=le5+10,INF=0x3f3f3f3f;
int dp[N],a[5];
int main()
{
    ios_base::sync_with_stdio(0);
    int T;
    cin>>T;
    a[0]=150;
    a[1]=200;
    a[2]=350;
    while(T--)
    {
        int n;
        cin>>n;
        for(int i=0;i<N;i++)
            dp[i]=0;
        for(int i=0;i<3;i++)
        {
            for(int j=a[i];j<=n;j++)
            {
                dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
            }
        }
        cout<<n-dp[n]<<endl;
    }
    
    return 0;
}

多重背包问题


有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大

基本算法


这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可,因为对于第i种物品有n[i]+1种策略:取0件,取1件……取n[i]件。令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值,则有状态转移方程:
f [ i ] [ v ] = m a x f [ i − 1 ] [ v − k ∗ c [ i ] ] + k ∗ w [ i ] ∣ 0 < = k ∗ w [ i ] < = n [ i ] f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*w[i]<=n[i]} f[i][v]=maxf[i1][vkc[i]]+kw[i]0<=kw[i]<=n[i]

const int maxn=100005;
int w[amxn],v[maxn],num[maxn];//w:重量 v:价值 num:数量 // w: weight, //v:value, num:number
int dp[maxn];
int V;//背包容量
//01背包
void ZeroOnePack(int w,int v)
{
 for(int j=v;j>=w;j--)
 {
   dp[j]=max(dp[j],dp[j-w]+v);
 }
}
//完全背包
void CompletePack(int w,int v)
{
    for(int j=w;j<=v;j++)
    {
        dp[j]=max(dp[j],dp[j-w]+v);
    }
}
//多重背包
void MultiplePack(int w,int v,int num)
{
    if(w*num>=V)
    {
        CompletePack(w,v);
    }
    else
    {
        int k=1;
        while(k<num)
        {
            ZeroOnePack(k*w,k*v);
            num-=k;
            k<<=1;
        }
     
        ZeroOnePack(num*w,num*v);
    }
}
//多重背包
for(int i=1;i<=n;i++)
{
  if(w[i]*a[i]>m)
   {
     for(int c=0;c<=m;c++)
    {
     if(c>=w[i])
      f[c]=max(f[c],f[c-w[i]]+v[i]);
    }
   }
   else
   {
    k=1;
    amount=a[i];
    while(k<amount)
    {
     for(int c=k*w[i];c>=0;c--)
     {
       if(c>=w[i])
       f[c]=max(f[c],f[c-w[i]]+k*v[i]);
     }
       amount-=k;
       k<<=1;
    }
    for(int c=amount*w[i];c>=0;c--)
    { 
     f[c]=max(f[c],f[c-w[i]]+amount*v[i]);
    }
   }
}

混和三重背包


如果将P01P02P03混合起来。也就是说,有的物品只可以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)。应该怎么求解呢?

01背包与完全背包的混合

for i=1…N
if 第i件物品属于01背包
for v=V…0
f[v]=max{f[v],f[v-c[i]]+w[i]};
else if 第i件物品属于完全背包
for v=0…V
f[v]=max{f[v],f[v-c[i]]+w[i]};

再加上多重背包

for i=1…N
if 第i件物品属于01背包
ZeroOnePack(c[i],w[i])
else if 第i件物品属于完全背包
CompletePack(c[i],w[i])
else if 第i件物品属于多重背包
MultiplePack(c[i],w[i],n[i])

时间限制: 1 s

空间限制: 256000 KB

题目等级 : 钻石 Diamond

【题目描述】Description

背包体积为V ,给出N个物品,每个物品占用体积为Vi,价值为Wi,每个物品要么至多取1件,要么至多取mi件(mi > 1) , 要么数量无限 , 在所装物品总体积不超过V的前提下所装物品的价值的和的最大值是多少?

【输入描述】 Input Description

第一行两个数N,V,下面N行每行三个数Vi,Wi,Mi表示每个物品的体积,价值与数量,Mi=1表示至多取一件,Mi>1表示至多取Mi件,Mi=-1表示数量无限

【输出描述】 Output Description

1个数Ans表示所装物品价值的最大值

【样例输入】 Sample Input

2 10

3 7 2

2 4 -1

【样例输出】 Sample Output

22

【数据范围及提示】 Data Size & Hint

对于100%的数据,V <= 200000 , N <= 200

https://www.cnblogs.com/xiaoningmeng/p/6068741.html
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int V,n;
int w[210],v[210],m[210];
int f[200010];
int main()
{
    scanf("%d%d",&n,&V);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&w[i],&v[i],&m[i]);
    for(int i=1;i<=n;i++)
    {
        if(m[i]==-1)//完全背包 
            for(int j=w[i];j<=V;j++)
                f[j]=max(f[j],f[j-w[i]]+v[i]);
        else//01与多重背包 
        {
            int x=m[i];
            for(int k=1;k<=x;k<<=1)
            {
                for(int j=V;j>=w[i]*k;j--)
                    f[j]=max(f[j],f[j-w[i]*k]+v[i]*k);
                x-=k;
            }
            if(x)
                for(int j=V;j>=w[i]*x;j--)
                    f[j]=max(f[j],f[j-w[i]*x]+v[i]*x);
        }
    }
    cout<<f[V];
    return 0;
}

二维费用的背包问题


二维费用的背包问题是指:对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价;对于每种代价都有一个可付出的最大值(背包容量)。问怎样选择物品可以得到最大的价值。设这两种代价分别为代价1和代价2,第i件物品所需的两种代价分别为a[i]和b[i]。两种代价可付出的最大值(两种背包容量)分别为V和U。物品的价值为w[i]。


算法

费用加了一维,只需状态也加一维即可。设f[i][v][u]表示前i件物品付出两种代价分别为v和u时可获得的最大价值。状态转移方程就是:
f [ i ] [ v ] [ u ] = m a x f [ i − 1 ] [ v ] [ u ] , f [ i − 1 ] [ v − a [ i ] ] [ u − b [ i ] ] + w [ i ] f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i]} f[i][v][u]=maxf[i1][v][u],f[i1][va[i]][ub[i]]+w[i]
https://blog.csdn.net/qq_33765907/article/details/50726151

分组的背包问题

有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选。也就是说设f[k][v]表示前k组物品花费费用v能取得的最大权值,则有:


f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i属于组k}
for 所有的组k
    for v=V..0
        for 所有的i属于组k
            f[v]=max{f[v],f[v-c[i]]+w[i]}
//https://www.cnblogs.com/zhangmingcheng/p/3940332.html

#include <iostream>
using namespace std;
int a[101][101],f[101];
int main()
{
    int n,m,i,j,k;
    while(cin >> n >> m && (n != 0 || m != 0))
    {
        memset(f,0,sizeof(f));
        for(i = 1; i <= n; i++)
            for(j = 1; j <= m; j++)
                cin >> a[i][j];
        for(i = 1; i <= n; i++) //第一重循环:分组数
            for(j = m; j >= 0; j--) //第二重循环:容量体积
                for(k = 0; k <= j; k++) //第三重循环:属于i组的k
                    f[j] = max(f[j],f[j-k]+a[i][k]);
        cout << f[m] << endl;
    }
    return 0;
}

有依赖的背包


这种背包问题的物品间存在某种“依赖”的关系。也就是说,i依赖于j,表示若选物品i,则必须选物品j。为了简化起见,我们先设没有某个物品既依赖于别的物品,又被别的物品所依赖;另外,没有某件物品同时依赖多件物品。

https://www.cnblogs.com/hrj1/p/11184616.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值