動態規劃 - 二維揹包
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;
}