【C++揹包】大煩惱混合三種揹包與二維費用的揹包問題

上一次,我們講了多重揹包問題,其中講的天花亂墜,希望所有的讀者能夠聽懂 :》
這次,我們將兩種揹包問題一起講了,就是讓人煩惱的混合揹包問題與二維費用的揹包問題。

混合揹包問題

經典題型

上次的旅行者,又要開始旅途了!誰知道他這次會去哪裏呢?他有N件物品,還有一個容量爲V的三級 包,重量w[i],價值c[i],有的醫療箱 物品可以取一次,有的醫療包 物品可以取n[i]次,有的繃帶 物品能無限取。同樣,讓他裝下的東西價值最多。所以,讓你編個程序幫助他。
天哪!這是坑人嗎? 有三大類物品,還混合了在一起!

其實,我們簡單思考一下,就可以將三者放到一起。考慮到01揹包與多重揹包極其相似,而完全揹包又正好能夠單獨放出來,那麼,那麼…(省下10001個那麼),就可以將01揹包,完全揹包與多重揹包分開來就可以了嘛。

所以,01揹包可以與多重揹包完美重合,而完全揹包可以另開一個規劃,所以,狀態轉移方程是相同的:f(v)=max(f(v),f(vw(i))+c(i))f(v)=max(f(v),f(v-w(i))+c(i))。是不是so easy?

僞代碼如下:

for(i=1...N)
	if 第i件物品是01揹包 或多重揹包
		for(j=1...n[i]) //最多多少個
			for(v=V...w[i])
				f[v]=max(f[v],f[v-w[i]]+c[i]);
	if 第i件物品是完全揹包
		for(v=w[i]...V)
			f[v]=max(f[v],f[v-w[i]]+c[i])

僞代碼出現,真實代碼也不遠了。稍微一擡指頭,便出現了下面的程序:

#include<iostream>
using namespace std;
int f[1000001],n[1000001],w[1000001],c[1000001];

int main()
{
	int N,V;
	cin>>V>>N;
	for(int i=1;i<=N;i++)
		cin>>w[i]>>c[i]>>n[i];
	for(int i=1;i<=N;i++)
	{
		if(n[i]!=0)
			for(int j=1;j<=n[i];j++) //最多多少個
				for(int v=V;v>=w[i];v--)
					f[v]=max(f[v],f[v-w[i]]+c[i]);
		else
			for(int v=w[i];v<=V;v++)
 				f[v]=max(f[v],f[v-w[i]]+c[i]);
 	}
 	int max=-99999999;
 	for(int i=1;i<=V;i++) max=max<f[i]? f[i]:max;
 	cout<<max;
 	return 0;
 }

效果還不錯

二維費用的揹包問題

意義

這次,我們先來說說二維費用的揹包問題的意義。二維費用?二維費用就是指的是對於每件物品,具有兩種費用;選擇這件物品必須同時付出這兩種代價;兩種代價!你會說,我想不出來有什麼生活中的例子有需要兩種代價!讓我告訴你吧,出題人總有想不完的想法,快趕得上作家了!
看着啦!

常見題型

【例n+4】潛水員
題目:潛水員爲了潛水要使用特殊的裝備。他有一個帶有兩種氣體的氣缸:一個爲氧氣,一個爲氮氣。讓潛水員下潛的深度需要各種數量的氧和氮。潛水員有一定數量的氣缸。每個氣缸都有重量和氣體容量。潛水員爲了完成他的工作需要特定數量的氧和氮。他完成工作所需的氣缸總重的最低限度是多少?
例如:潛水員有5個氣缸。每行三個數爲:氧、氮的(升)量和氣缸的重量:

3 36 120
10 25 129
5 50 250
1 45 130
4 20 119

如果潛水員需要5升的氧和60升的氮則總重最小爲249(1,2號氣缸或者4,5號氣缸)。
你的任務就是計算潛水員爲了完成他的工作需要的氣缸的重量的最低值。
【輸入格式】
第一行有2整數m,n(1<=m<=21,1<=n<=79)。它們表示氧,氮各自需要的量。
第二行爲整數k(1<=n<=1000)表示氣缸的個數。
此後的k行,每行包括ai,bi,ci(1<=ai<=21,1<=bi<=79,1<=ci<=800)3整數。這些各自是:第i個氣缸裏的氧和氮的容量及汽缸重量。
【輸出格式】
僅一行包含一個整數,爲潛水員完成工作所需的氣缸的重量總和的最低值。
【輸入樣例】

5 60
5
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119

【輸出樣例】

249

算法分析

費用增加了一維,那麼我們狀態也只需要增加一維即可。設f[i][v][u] 表示前i件物品付出的兩種代價分別爲v和u時可以獲得的最大的價值。所以,狀態轉移方程:
f(i)(v)(u)=max(f(i1)(v)(u),f(i1)(va(i))(ub(i))+c(i)) f(i)(v)(u)=max(f(i-1)(v)(u),f(i-1)(v-a(i))(u-b(i))+c(i))
如果使用之前分析的方法來,可以將數組縮減成二維數組:當每件物品只可以取一次時變量v和u採用逆序的循環,當物品有如完全揹包問題時採用順序的循環,多重揹包拆分物品。

有時,“二維費用”限制了一個最多能取M件物品。這實際上相當於每件物品多了一個“件數”的費用,費用均爲1,可以付出最大數費用爲M。
聽不懂是吧?
換句話說一說,設f[v][m]表示費用爲v,最多選m件時可得到的最大價值,價值則根據物品類型進行不同的方法循環更新,最有在f[0…v][0…m]的範圍內尋找答案。
另外,如果是要求恰好取m件物品,就在f[0…v][m]裏尋找答案。

直接給代碼:

#include<cstdio>
#include<cstring>
using namespace std;
int v, u, k;
int a[1001], b[1001], c[1001];
int f[101][101];
int main()
{
    memset(f,127,sizeof(f));
    f[0][0] = 0;
    scanf("%d%d%d",&v,&u,&k);
    for (int i = 1; i <= k; i++)
        scanf("%d%d%d",&a[i],&b[i],&c[i]);
    for (int i = 1; i <= k; i++)
      for (int j = v; j >= 0; j--)
        for (int l = u; l >= 0; l--)
        {
           int t1 = j+ a[i],t2 = l + b[i];
           if (t1 > v)  t1 = v;                        //若氮、氧含量超過需求,可直接用需求量代換,
           if (t2> u)  t2 = u;
           if (f[t1][t2] > f[j][l] + c[i])  f[t1][t2] = f[j][l] + c[i];
        }
    printf("%d",f[v][u]);
    return 0;
}

啊哈,今天的文章寫完了,真舒服啊!也請大家多多支持,多多指出問題!謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章