【C++揹包】生活中不可能出現的揹包問題

上次,我們講了最基礎最基礎…的01揹包問題,這次,我們就來講一下關於生活中不可能出現的揹包問題

無敵完全揹包問題

經典問題

【例n+1】之前的旅行者回來了。他勝利歸來,帶來了很多很多東西。又過了幾天,他的手的腳又開始癢癢了。於是,他又開始準備東西,繼續去探險。他有N種不同物品,又有一個容量爲V的揹包,每件物品都可以無限拿,。讓這位旅行者獲得最大的生存機率,然後又使揹包不會超限,請變出編出程序,幫助他。

提前透露下哈,這人姓魯。至於名,自己猜吧。

基本思路

這個問題,非常不接近接近01揹包問題,所不同的是每件物品有無限種!!!(天哪)從每件物品的角度去思考,不僅僅是拿和不拿,而是拿一件,兩件,······(無限件) 。如果按照解01揹包問題的思路,f[i][v]表示的是前i種物品放入一個容量爲v的揹包的最大利益。所以,按照這個思路,可以根據每種物品不同的策略寫出狀態轉移方程,like this:
f(i)(v)=max(f(i1)(vkw(i))+kc(i)0<kw(i)<=v) f(i)(v)=max(f(i-1)(v-k*w(i))+k*c(i)|0<k*w(i)<=v)
這樣,根據01基礎揹包問題的思路就求出了這樣不清晰 清晰的方法。

優化

這個算法使用一維數組的話,根據01揹包問題改進版,僞代碼如下:

for(i=1...N)
	for(v=0...V)
		f[v]=max(f[v],f[v-w[i]]+c[i]);

誒亞,這和01揹包問題只是差一個V次循環的方向而已。爲什麼這一改就可以了呢?首先回顧下上次01揹包問題爲什麼要進行逆序搜尋。

在這裏,我們先考慮上面的思路怎樣實現。肯定有一個主循環for(1…n)每次算出來二維數組f[i][0~V]的所有值。如果只用一個數組f[0…V]能不能保證第i次循環完後f[v]中表示着f[i][v]呢?實際,這要求在每次主循環中我們以v=V…0的逆序推f[v],這樣能保證f[v]時f[v-w[i]]保存的是狀態f[i-1][v-w[i]]的值。所以,僞代碼如下:
for(i=1…N)
for(v=V…0)
f[v]=max{f[v],f[v-w[i]]+c[i]};

重點來了,“實際,這要求在每次主循環中我們以v=V…0的逆序推f[v],這樣能保證f[v]時f[v-w[i]]保存的是狀態f[i-1][v-w[i]]的值”。於是,我們就能看出,這是因爲要保證第i次循環中的狀態f[i][v]是由狀態f[i-1][v-w[i]]推導而來。換句話說,這正是爲了保證每件物品只能選擇1次,保證在考慮“選入第i件物品”這個策略時,依據的是一個不是已經放入了第i件物品的子結果f[i-1][v-w[i]]。
看得懂嗎? 我似乎也看不懂。。。
讓我們簡單來說明吧。狀態表:設有3件物品,價值分別爲1,2,3,重量分別爲2,3,1,揹包重量爲5。這時,從01揹包思想考慮,雙逆推產生的狀態表:

i 3 2 1
v 5,3 5,3,2,0 5,3,2,2,1,0
f 0,1 0,1,2,5 0,1,2,4,5,3

f[v]=max{f[v],f[v-w[i]]+c[i]} 在雙逆推中,f[v]不可能是由f[v]本身推出。
逆推+順推

i 1 2 3
v 1,3,5 //此處1,3,5都推出,向右看: 0,2 0 //此處的0是由兩個1號+1個3號得來 ,0,1,1,2,2,3,4,5
f 1,0

f[v]=max{f[v],f[v-w[i]]+c[i]}在單順單逆中,f[v]可能是由f[v]本身推出。
這下看懂了吧。

繼續優化

根據上次for優化經驗,優化v=V…w[i],同樣,沒得放了,你還放個啥?
僞代碼如下:

for(i=1...N)
	for(v=w[i]...V)
		f[v]=max(f[v],f[v-w[i]]+c[i]);

經過了這複雜難懂的思考,最後的代碼如下:

#include<iostream>
#include<cstdio>
using namespace std;
int thing[1000001][3];         //如有數據內容不懂,請看上篇文章
int max(int a,int b)
{
	return a>b? a:b;
}
int main()
{
	int n,V;
	cin>>V>>n;
	for(int i=1;i<=n;i++) cin>>thing[i][0]>>thing[i][1];
 	for(int i=1;i<=n;i++)
		for(int v=thing[i][0];v<=V;v++)
		{
			thing[v][2]=max(thing[v][2],thing[v-thing[i][0]][2]+thing[i][1]);
		}
	cout<<thing[V][2];
	return 0;
}

欸喲,寫一篇文章真累真舒服啊,請大家多多關注我的文章,有什麼問題可以多多提出。

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