C++ 动态规划-背包类

来源:互联网 发布:家用网络交换机连接 编辑:程序博客网 时间:2024/05/16 10:30

(一)01背包

有个背包,可以装重为V的物品。有n种物品,第i个物品的重量(或说是空间)是v[i], 价值是c[i],每种物品只有一件,求能装的最大价值

 

二维解法:

定义二维数组f[i][j],表示前i件 不超过j的物品的最大价值


动态转移方程:(根据第i件物品取或不取)

f[i][j] = max(f[i-1][j], f[i-1][j-w[i]] + c[i]);

文字的解释:

i件重不超过j的物品的最大价值=取下面两项之最优

1 f[i-1][j]表示前i-1件重j的物品的最大价值,也就是 第i件没取,前i件的最大价值就是前i-1件的最大价值;

2 f[i-1][j-w[i]] + c[i] 表示前i-1件重j-w[i]的物品的最大价值 也就是第i件取了,占了w[i]的重量,增加了c[i]的价值

解是 f[n][V]  (前n个物品重不超过V的最大价值)

 

#include <iostream>using namespace std;int n, V;int v[200], c[200];int f[1001][1001];  // f[i][j] --> 前i个物品,重量不超过j的最大价值 int main() {    cin >> V >> n;    for(int i=1; i<=n; i++) cin >> v[i] >> c[i];    for(int i=1; i<=n; i++) { //循环前i件物品        for(int j=V; j>0; j--) { //循环重量j            if(v[i] <= j) f[i][j] = max(f[i-1][j], f[i-1][j-v[i]] + c[i]);            else f[i][j] = f[i-1][j];      //放不下         }    }    cout << f[n][V] << endl;    return 0;}


一维解法:

定义数组f[j], 表示重不超过j的物品的最大价值

动态转移方程:(根据第i件物品取或不取)

f[j] = max(f[j], f[j-w[i]] + c[i]);

j循环必须是j=V...0

#include <iostream>using namespace std;int n, V;int v[200], c[200];int f[1001];  // f[j] --> 重量不超过j的最大价值 int main() {    cin >> V >> n;    for(int i=1; i<=n; i++) cin >> v[i] >> c[i];    for(int i=1; i<=n; i++) {        //循环前i件物品        for(int j=V; j>=v[i]; j--) { //循环重量j (j<v[i]时放不下,不操作)            f[j] = max(f[j], f[j-v[i]] + c[i]);           }    }    cout << f[V] << endl;    return 0;}




(二)完全背包

有个背包,可以装重为V的物品。有n种物品,第i个物品的重量是v[i], 价值是c[i],

每种物品有无限件,求能装的最大价值



一维解法:

定义数组f[j], 表示重不超过j的物品的最大价值

动态转移方程:同01背包一维解法一样(根据第i件物品取或不取)

f[j] = max(f[j], f[j-w[i]] + c[i]);

01不同的是:

j循环必须是j=0...V

重不超过j的物品的最大价值

解是f[V](重量不超过V的最大价值)


#include <iostream>using namespace std;int n, V;int v[200], c[200];int f[1001];  // f[j] --> 重量不超过j的最大价值int main() {cin >> V >> n;for(int i=1; i<=n; i++) cin >> v[i] >> c[i];for(int i=1; i<=n; i++) {        //循环前i件物品for(int j=v[i]; j<=V; j++) { //循环重量j (j<v[i]时放不下,不操作)f[j] = max(f[j], f[j-v[i]] + c[i]);}}cout << f[V] << endl;return 0;}



(三)多重背包

有个背包,可以装重为V的物品。有n种物品,第i个物品的重量是v[i], 价值是c[i],

每种物品有s[i]件,求能装的最大价值


一维解法:

定义数组f[j], 表示重不超过j的物品的最大价值

动态转移方程:(根据第i件物品取或不取)

i=1...n;

   j=V...0;

      k=0...s[i]     //K循环件数s[i]

     f[j] = max(f[j], f[j-w[i]*k] + c[i]*k);

 (多重背包与01背包一样:j循环是j=0...V)

解是f[V];



#include <iostream>using namespace std;int n, V;int v[200], c[200], s[200];int f[1001];  // f[j] --> 前需要占重量不超过j的最大价值int main() {cin >> V >> n;for(int i=1; i<=n; i++) cin >> v[i] >> c[i] >> s[i];for(int i=1; i<=n; i++) {for(int j=V; j>=0; j--) {for(int k=0; k<=s[i]; k++) {if(j-v[i]*k < 0) break; //k是递增的,背包不够k循环结束f[j] = max(f[j], f[j-v[i]*k] + c[i]*k);}}}cout << f[V] << endl;return 0;}




(四)混合背包

有个背包,可以装重为V的物品。有n种物品,第i个物品的重量是v[i], 价值是c[i],

有的物品有1件,有的有s[i]件,有的有无限件,求能装的最大价值


解法:

i=1...n;

if(第i件是完全背包) ..

else if(第i件是多重背包) ..

//只能取1件作为s[i]为1的物品

 

可以根据混合情况变通if语句

 

三种背包-混合背包:


#include <iostream>using namespace std;int n, V;int v[1001], s[1001], c[1001];int f[1001];int main() {cin >> V >> n;for(int i=1; i<=n; i++) cin >> v[i] >> c[i] >> s[i];for(int i=1; i<=n; i++) {if(s[i] == 0) {for(int j=v[i]; j<=V; j++)f[j] = max(f[j], f[j-v[i]]+c[i]);} else {for(int k=1; k<=s[i]; k++)for(int j=V; j>=v[i]; j--)f[j] = max(f[j], f[j-v[i]] + c[i]);}}cout << f[V] << endl;return 0;}



(五)二维费用背包

二维费用只是直接在原有背包上加一维度和循环

 

1原来f[j]表示V不超过j的最大价值

现在f[j][k]表示V1不超过j且V2不超过k的最大价值

 

2 原来f[j] = max(f[j],f[j-v[i]] + c[i]);

现在f[j][k] = max(f[j][k], f[j-v1[i]][k-v2[i]] + c[i]);

 

这样连三维费用背包都会了..

 

这里直接是二维费用的混合三种背包

 



#include <iostream>using namespace std;int n, V1, V2;int v1[1001], v2[1001], m[1001], c[1001];int f[1001][1001];int main() {    cin >> n >> V1 >> V2;    for(int i=1; i<=n; i++) cin >> v1[i] >> v2[i] >> m[i] >> c[i];    for(int i=1; i<=n; i++) {        if(m[i] == 0) { //如果第i件是完全背包             for(int j=v1[i]; j<=V1; j++) {                 for(int k=v2[i]; k<=V2; k++) {                    f[j][k] = max(f[j][k], f[j-v1[i]][k-v2[i]] + c[i]);                }            }        } else { //如果第i件数量有限(01或多重)             for(int j=V1; j>=v1[i]; j--) {                for(int k=V2; k>=v2[i]; k--) {                    for(int s=1; s<=m[i]; s++) {                        if(j - s*v1[i] < 0 || k - s*v2[i] < 0) break;                        f[j][k] = max(f[j][k], f[j-v1[i]*s][k-v2[i]*s] + c[i]*s);                    }                }            }        }    }    cout << f[V1][V2] << endl;    return 0;}


(六)有依赖性的背包

 

【金明的预算方案】

 

【解法】分五种情况处理主件:不取,只取主件,取主件和附件1,取主件和附件2,取主件和两个附件.找最优情况

 

#include <iostream>using namespace std;int V, n, num;     //V是金钱,n是物品总数,num记录主件数 int v[61][4];      //v[i][0]记录有几个附件了,v[i][1]:主件的价格,v[i][2]:附件1,v[i][3]:附件2 int p[61][4];      //同上,记录重要度 int f[61][32001];  //f[i][j]表示前i件主件价格为j的最大价值 int map[61];       //i映射到v、p数组(num) int main() {int tmpv, tmpp, follow;cin >> V >> n;for(int i=1; i<=n; i++) {cin >> tmpv >> tmpp >> follow;if(follow == 0) {v[++num][1] = tmpv;p[num][1] = tmpp;map[i] = num;continue;}follow = map[follow];v[follow][++v[follow][0]+1] = tmpv;p[follow][++p[follow][0]+1] = tmpp;}for(int i=1; i<=num; i++) {for(int j=1; j<=V; j++) {f[i][j] = f[i-1][j];if(j >= v[i][1]) f[i][j] = max(f[i][j], f[i-1][j-v[i][1]]+v[i][1]*p[i][1]);if(j >= v[i][1]+v[i][2]) f[i][j] = max(f[i][j], f[i-1][j-v[i][1]-v[i][2]]+v[i][1]*p[i][1]+v[i][2]*p[i][2]);if(j >= v[i][1]+v[i][3]) f[i][j] = max(f[i][j], f[i-1][j-v[i][1]-v[i][3]]+v[i][1]*p[i][1]+v[i][3]*p[i][3]);if(j >= v[i][1]+v[i][2]+v[i][3]) f[i][j] = max(f[i][j], f[i-1][j-v[i][1]-v[i][2]-v[i][3]]+v[i][1]*p[i][1]+v[i][2]*p[i][2]+v[i][3]*p[i][3]);}}cout << f[num][V] << endl;return 0;}

 

(七)分组背包

N件物品,第i个物品的重量是v[i], 价值是c[i],每个物品有自己的组,每组只能选一个物品,求能装的最大价值



QWQ,就这样了...#end

原创粉丝点击