C++解决背包问题

感谢https://blog.csdn.net/Hearthougan/article/details/53869671html

只许选一次的背包 –– 0-1背包ios

问题描述:web

N N N个物品和一个容量为 S S S的背包,第 i i i件物品的重量是 w [ i ] w[i] w[i],价值是 v [ i ] v[i] v[i]。在每种物品只许放一次,不可拆分,不超过背包容量的前提下,问如何才能让背包的总价值最大。数组

思路:svg

咱们将 f [ i ] [ j ] f[i][j] f[i][j]定义为在前 i i i个物品里作出选择,使容量为 j j j的背包价值最大。优化

对于第 i i i件物品,有两种状况:spa

1.放物品 i i i,此时 f [ i ] [ j ] = f [ i − 1 ] [ j − w [ i ] ] + v [ i ] f[i][j]=f[i-1][j-w[i]]+v[i] f[i][j]=f[i1][jw[i]]+v[i]
2.不放物品 i i i,此时 f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j]=f[i-1][j] f[i][j]=f[i1][j].net

综上所述,本题的状态转移方程为: f [ i ] [ j ] = m a x f [ i − 1 ] [ j − w [ i ] ] + v [ i ] , f [ i − 1 ] [ j ] f[i][j]=max{f[i-1][j-w[i]]+v[i],f[i-1][j]} f[i][j]=maxf[i1][jw[i]]+v[i]f[i1][j]设计

伪代码以下:code

for 1...n
    for w[i]...s
        f[i][j]=max{ 
 
  f[i-1][j-w[i]]+v[i],f[i-1][j]}

这个思路时间上是最优的,可是空间能够优化到 O ( V ) O(V) O(V)

毕竟 f [ i ] [ j ] f[i][j] f[i][j]只跟上一行有关系,那么咱们能够试试只用一维数组去储存。

这种方法可不可行?可行,可是 j j j要逆向枚举,由于旧数据不能被覆盖。

由于 f [ j ] f[j] f[j]依赖于 f [ j − w [ i ] ] f[j-w[i]] f[jw[i]](旧数据哦),若是正向枚举的话, f [ j − w [ i ] ] f[j-w[i]] f[jw[i]]就已经被新数据覆盖掉了,就有可能已经选入了物品i!(别忘了 f [ i ] [ j ] f[i][j] f[i][j]是指在前i个物品里作出选择)

也就是说,原先的 f [ i − 1 ] [ j − w [ i ] ] f[i-1][j-w[i]] f[i1][jw[i]]已经被覆盖了,如今里面装的是 f [ i ] [ j − w [ i ] ] f[i][j-w[i]] f[i][jw[i]]。这时极有可能已经选中了物品 i i i,若是此次物品 i i i还被选中的话,物品 i i i就被选中了两次,违背了0-1背包的限制!

所以,为了避免违背0-1背包的限制,咱们必须逆向枚举。

优化后的伪代码以下:

for 1...n
    for s...w[i]
           f[j]=max{ 
 
  f[j-w[i]]+v[i],f[j]}

例题:

开心的金明

金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他本身专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间须要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”。今天一早金明就开始作预算,可是他想买的东西太多了,确定会超过妈妈限定的N元。因而,他把每件物品规定了一个重要度,分为5等:用整数1~5表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。他但愿在不超过N元(能够等于N元)的前提下,使每件物品的价格与重要度的乘积的总和最大。

设第j件物品的价格为v[j],重要度为w[j],共选中了k件物品,编号依次为j1...jk,则所求的总和为:v[j1]*w[j1]+..+v[jk]*w[jk]。

请你帮助金明设计一个知足要求的购物单。

【输入格式】
输入的第1行,为两个正整数N,M,用一个空格隔开:(其中n(n<30000)表示总钱数,m(m<25)为但愿购买物品的个数。)

从第2 行到第m+1行,第j行给出了编号为j-1的物品的基本数据,每行有2个非负整数v,p
(其中v 表示该物品的价格(v≤10000),p 表示该物品的重要度(1~5)) 

【输出格式】
输出只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<100000000)。

【输入样例】
1000 5
800 2
400 5
300 5
400 3
200 2
【输出样例】
3900

代码:

#include<iostream>
#include <algorithm>
using namespace std;
struct thing { 
 
  
   int v,p;
} want[30];
long long c[10010],d[10010];
int main() { 
 
  
   int n,m;
   cin>>n>>m;
   for(int i=1; i<=m; i++) { 
 
  
       cin>>want[i].v>>want[i].p;
   }
   for(int i=1; i<=m; i++) { 
 
  
       for(int j=n; j>=want[i].v; j--) { 
 
  
           c[j]=max(c[j],c[j-want[i].v]+want[i].v*want[i].p);
       }
   }
   cout << c[n] << endl;
   return 0;
}

能够选无数次的背包 –– 彻底背包

问题描述:

N N N个物品和一个容量为 S S S的背包,第 i i i件物品的重量是 w [ i ] w[i] w[i],价值是 v [ i ] v[i] v[i]。在每种物品有无限个,不可拆分,不超过背包容量的前提下,问如何才能让背包的总价值最大。

此题和0-1背包问题很像,因此能够在0-1背包的基础上作必定的修改就能够了。

想一想看,0-1背包在哪里阻止了重复选择物品的可能?

固然是 j j j的循环这个地方!

那么,咱们直接把 j j j的循环这个地方改为正序(可能重复装入物品,正好知足要求)不就能够了吗?

完整代码以下(找不到例题QAQ)

#include<iostream>
#include<algorithm>
using namespace std;
struct thing { 
 
   
   int v,p;
} want[30];
long long c[10010];
int main() { 
 
   
    int n,m,;    
    cin>>n>>m;    
    for(int i=1; i<=m; i++) { 
 
   
          cin>>want[i].v>>want[i].p;    
    }    
    for(int i=1; i<=m; i++) { 
 
          
        for(int j=want[i].v;j<=n; j++) { 
 
            
            c[j]=max(c[j],c[j-want[i].v]+want[i].p);       
          }   
     }    
     cout << c[n] << endl; 
     return 0;
}

数量有上限的背包 –– 多重背包

问题描述:有 N N N个物品和一个容量为 S S S的背包,第 i i i件物品的重量是 w [ i ] w[i] w[i],价值是 v [ i ] v[i] v[i],上限是 c [ i ] c[i] c[i]。在不可拆分,不超过背包容量的前提下,问如何才能让背包的总价值最大。

此题和0-1背包问题也很像,因此也能够在0-1背包的基础上作必定的修改。

咱们能够直接加一个循环,从0到上限枚举物品个数,伪代码以下:

for 1...n 
   for s...w[i]  
       for 0...c[i]
         if j>=w[i]*k
          f[j]=max{ 
 
  f[j-w[i]*k]+v[i]*k,f[j]}

例题:

庆功会

为了庆祝班级在全校运动会上取得全校第一名的成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员。指望拨款金额可以买最大值的奖品,能够补充他们的精力和体力。
【输入格式】
第一行两个数n(≤500),m(≤6000),分别表明奖品种数和拨款金额。
接下来n行,每行三个数v,p,s,分别表明奖品价格,价值和可以买到的最大数量,其中v≤100,p≤1000,s≤10。
【输出格式】
一个数,表示这次购买能得到的最大价值。
【输入样例】
5 1000
80 20 4
40 50 9
30 50 7
40 30 6
20 20 1
【输出样例】
1040
#include<iostream>
#include<algorithm>
using namespace std;
struct thing { 
 
   
  int v,p,s;
} want[30];
long long c[10010];
int main() { 
 
   
  int n,m;       
  cin>>n>>m;      
  for(int i=1; i<=m; i++) { 
 
  
             cin>>want[i].v>>want[i].p>>want[i].s;        
  }        
  for(int i=1; i<=n; i++) { 
 
   
     for(int j=m;j>=want[i].v; j--) { 
 
  
         for(int k=0;k<=want[i].s; k++){ 
 
  
              if(j >=k*want[i].v)
                 c[j]=max(c[j],c[j-k*want[i].v]+k*want[i].p);      
        }          
     }       
  }          
  cout << c[m] << endl;      
  return 0;
}

附:
0-1背包空间和时间都最优的代码

#include <iostream>
#include <algorithm>
using namespace std;
int main(){ 
 
  
    int n,s,f[100],w,v;
    cin>>n>>s;
    for(int i = 1;i <= n;i++)
        cin>>w>>v;
        for(int j = s; j >= w[i]; j--)
            f[j] = max(f[j], f[j-w]+v);
    cout<<f[s]<<endl;
    return 0;
}

超级大礼包

https://www.luogu.com.cn/discuss/show/263072