动态规划 - 二维背包
1.普通二维(费用)背包
01背包问题:给定 n 种物品和一个重量(容量)(限定条件)为 w 的背包,物品 i 的重量是 wi,其价值为 vi。(每种物品只有一个)问:如何选择装入背包的物品,使得装入背包中的物品的价值最大。
二维背包:给定 n 种物品和两个限定条件【最大重量为max_w(weight),最大体积为max_b(bulk)】的背包,物品 i 的重量是 wi,体积是bi,其价值为 vi。(每种物品只有一个)问:如何选择装入背包的物品,使得装入背包中的物品的价值最大。其在代码实现上相对01背包来说,给动态规划表再加一维即可。
//VC6.0--------------------------
#include "stdafx.h"
#include <cmath>
#include <iostream>
using namespace std;
//----------全局变量(输入信息)------------
#define n 5 //数量
#define max_w 7 //最大重量
#define max_b 9 //最大体积
int v[n]={1,2,3,4,5}; //价值
int w[n]={2,3,4,5,6}; //重量
int b[n]={3,4,5,6,7}; //体积
//----------最大值函数--------
int max(int a,int b){
if(a>b) return a;
else return b;
}
//-----------------------填写动态规划表-----------------------
int beibao_2(int *x)
{
int i,j,k;
int real_w,real_b; //实际重量 体积
int dpt[n][max_w+1][max_b+1]={0}; //动态规划表 三维
int max_v=0; //最大价值
//--------------第一面------------
for (j = 0; j <= max_w; j++)
for (k = 0; k <= max_b; k++)
if((j >= w[0]) && (k >= b[0]))
dpt[0][j][k] = v[0];
//--------------其余面------------
for (i = 1; i < n; i++)
{
for (j = 1; j <= max_w; j++)
{
for (k = 0; k <= max_b; k++)
{
if ((j < w[i]) || k < b[i])
dpt[i][j][k] = dpt[i-1][j][k];
else
{
dpt[i][j][k] = max(dpt[i-1][j][k], dpt[i-1][j-w[i]][k-b[i]] + v[i]);
}
}
}
}
//求最大价值 以及实际重量
for (j = 1; j <= max_w; j++)
{
for (k = 0; k <= max_b; k++)
{
if(dpt[n-1][j][k] > max_v)
{
max_v = dpt[n-1][j][k];
real_w = j;
real_b = k;
}
}
}
//求一个最优解
for (i = n-1; i > 0; i--)
{
if(dpt[i][real_w][real_b] != dpt[i-1][real_w][real_b])
{
x[i] = 1;
real_w = real_w-w[i];
real_b = real_b-b[i];
}
}
if(real_w != 0)
x[0] = 1;
//返回最大值
return max_v;
}
//------------------------------------------------------
void main()
{
//解向量
int x[n]={0};
//输出答案
cout<<"最大价值:"<<beibao_2(x)<<endl;
cout<<"解向量:";
for(int k=0;k<n;k++)
cout<<x[k]<<" ";
cout<<endl;
}
2.潜水员问题
潜水员为了潜水要使用特殊的装备。他有一个带2种气体的气缸:一个为氧气,一个为氮气。让潜水员下潜的深度需要各种的数量的氧和氮。潜水员有一定数量的气缸。每个气缸都有重量和气体容量。潜水员为了完成他的工作需要特定数量的氧和氮。他完成工作所需气缸的总重的最低限度的是多少? 例如:潜水员有5个气缸。每行三个数字为:氧,氮的(升)量和气缸的重量: 3 36 120 10 25 129 5 50 250 1 45 130 4 20 119 如果潜水员需要5升的氧和60升的氮则总重最小为249 (1,2或者4,5号气缸)。 你的任务就是计算潜水员为了完成他的工作需要的气缸的重量的最低值。
---------------------------问题摘自链接:https://blog.csdn.net/github_39329077/article/details/82778506
问题分析:类似普通二维背包,给定n个带有两种气体【a气体和b气体】的气罐,气罐i的a气体含量是ai,b气体含量是bi,重量为wi,如何选择气罐 使a气体不小于(大于等于)max_a,b气体不小于max_b,并使所选气罐总重量最小。
相同点:普通二维背包 重量 体积 价值 类似于气罐的 a气体 b气体 重量
不同点:1.背包价值要求最大 气罐重量要求最小 状态转移方程需要修改求最小值
2.背包的重量和体积有最大限制 气罐两种气体的含量只要满足一定量即可,即有最小限制
二维背包dpt[i][j][k]=c 的意义为前i个物品 重量体积不超过j和k时其所含最大价值为 c
潜水员问题 dpt[i][j][k]=c 的意义为前i个气罐 a b气体含量至少为j和k时其最小重量为c
//VC6.0--------------------------
#include "stdafx.h"
#include <cmath>
#include <iostream>
using namespace std;
//----------全局变量(输入信息)------------
#define n 5 //数量
#define max_a 5 //最少需要a气体的量 动态规划表第二维最大值
#define max_b 60 //最少需要b气体的量 动态规划表第三维最大值
int w[n]={120,129,250,130,119}; //重量
int a[n]={3,10,5,1,4}; //重量
int b[n]={36,25,50,45,20}; //体积
//----------最小值函数--------
int min(int a,int b){
if(a>b) return b;
else return a;
}
//-----------------------填写动态规划表-----------------------
int beibao_2(int *x)
{
int i,j,k;
int real_a,real_b; //气体实际含量
int dpt[n][max_a+1][max_b+1]; //dpt[i][j][k]=c 的意义为前i个气罐 a b气体含量至少为j和k时其最小重量为c
//初始化 由于其求最小值 若初始化0则会误判 (不符合上述dpt[i][j][k]=c 的意义)
for (i = 0; i < n; i++)
{
for (j = 0; j <= max_a; j++)
{
for (k = 0; k <= max_b; k++)
{
dpt[i][j][k] = 10000; //较大的值
}
}
}
//---------------第一面---------------
for (j = 0; j <= max_a; j++)
{
for (k = 0; k <= max_b; k++)
{
//第1个气罐 至少含有j k容量的a b气体 则重量为第1个气罐的重量
if(j < a[0] && k < b[0])
dpt[0][j][k] = w[0];
}
}
dpt[0][0][0] = 0; //需要注意的特殊点 没有气体的气罐最小重量是0
//---------------其余面------------
int aa,bb;
for (i = 1; i < n; i++)
{
for (j = 1; j <= max_a; j++)
{
for (k = 1; k <= max_b; k++)
{
aa = j + a[i];
bb = k + b[i];
//大于最大值 即都符合条件 存储到一处即可
if(aa > max_a) aa = max_a;
if(bb > max_b) bb = max_b;
//dpt[i][aa][bb] = min(dpt[i-1][aa][bb],dpt[i-1][j][k]+w[i]);
//由于有多种超出满足范围的情况 需要与前一情况进行比较 故需求三个数的最小值
dpt[i][aa][bb] = min(min(dpt[i-1][aa][bb],dpt[i][aa][bb]),dpt[i-1][j][k]+w[i]);
}
}
}
//求一个最优解 //本来想求实际气体含量 但是太晚了有点困 就这样简单求了
real_a = max_a;
real_b = max_b;
for (i = n-1; i > 0; i--)
{
if(dpt[i][real_a][real_b] != dpt[i-1][real_a][real_b])
{
x[i] = 1;
real_a = real_a-a[i];
real_b = real_b-b[i];
//减完之后 若小于前一个气罐的气体含量 则仅有前一个气罐
if(real_a < a[i-1] && real_b < b[i-1])
{
x[i-1] = 1;
break;
}
}
}
//返回最小重量
return dpt[n-1][max_a][max_b];
}
//------------------------------------------------------
void main()
{
//解向量
int x[n]={0};
//输出答案
cout<<"最小重量:"<<beibao_2(x)<<endl;
cout<<"解向量:";
for(int k=0;k<n;k++)
cout<<x[k]<<" ";
cout<<endl;
}