洛谷題集——裝箱問題(暴力dfs、0/1揹包通俗版)

前言:這道題可以說是最經典的0/1揹包問題
最近算是第一次正式遇到0/1揹包問題(以前都是暴力求解),這篇文章主要將這種類型的題目講細、講清楚。

裝箱問題

有一個箱子容量爲VV(正整數,0 ≤ V ≤ 20000),同時有n個物品(0 < n < 30,每個物品有一個體積(正整數)。
要求n個物品中,任取若干個裝入箱內,使箱子的剩餘空間爲最小。
輸入格式
1個整數,表示箱子容量
1個整數,表示有n個物品
接下來n行,分別表示這n個物品的各自體積
輸出格式
1個整數,表示箱子剩餘空間。

輸入輸出樣例
輸入
24
6
8
3
12
7
9
7
輸出
0


題目所求:最小剩餘空間 = 揹包空間 - 揹包最多能裝的空間
題目與其是求最小剩餘空間,不如說是求該箱子最多能裝多少空間。
這道最普通的方法就是dfs暴力解法。

暴力dfs解法

主要思路:模擬每個物品按不同順序裝入揹包的情況。這種思路會導致有很多裝入的順序不同,但是最終能裝入揹包的空間是相同的。
可能該代碼能夠進行部分剪枝優化,但是仍然無法完全避免多次重複判斷的情況,從而造成程序運行超時。

#include<bits/stdc++.h>
using namespace std;
int V, n, v[35], ans=20001;
int judge[35]={false};
void dfs(int yy_v)
{
	ans = min(ans, V-yy_v);
	
	for(int i=0; i<n; i++)
	{
		if(judge[i]==false && yy_v+v[i]<=V)
		{
			judge[i]=true;
			dfs(yy_v+v[i]);
			judge[i]=false;
		}
	}
}

int main()
{
	cin>>V>>n;
	for(int i=0; i<n; i++)
		cin>>v[i];
	
	for(int i=0; i<n; i++)
	{
		if(V>=v[i])
		{
			judge[i]=true;
			dfs(v[i]);
			judge[i]=false;
		}
	}
	cout<<ans;
	
	return 0;
}

0/1揹包

個人認爲:0/1揹包思路一想通了,就會容易理解,然後多練習類似的題型鞏固知識,就能掌握。

//代碼核心邏輯
	for(int i=0; i<n; i++)
	{
		for(int j=V; j>=v[i]; j--)
		dp[j] = max(dp[j-v[i]]+v[i], dp[j]);
	}
解題思路

首先我們看dp數組在該代碼中扮演的角色——不同類型的揹包。
dp[j] :揹包總空間爲 j 的揹包。 v[i] :第 i 件物品的空間大小。

步驟一:我們將每個物品都嘗試放入不同大小的揹包。

步驟二: 當該 dp[j] 揹包的總空間 j >= v[i],dp[j]揹包在空包的條件下,能裝得下中物品v[i]。
反之如果 j < v[i],dp[j]揹包無論如何都裝不得下中物品v[i]。
(ps:這也是爲什麼在代碼中 j 要由大到小的原因)

如何推出狀態轉移方程:dp[j] = max(dp[j - v[i]] + v[i], dp[j])?
讓我們想一想,如果揹包 dp[j] 本身就裝了很多空間,它裝的空間甚至比揹包dp[j - v[i]] 加上 v[i] 還要多,聰明的我們肯定是保留 dp[j] 本身的值,才能使揹包dp[j] 裝的空間最多。
反之,如果 dp[j] 本身裝的空間少於揹包dp[j - v[i]] 加上 v[i],那揹包 dp[j] 就能裝更多的空間,並且裝的物品空間就是揹包dp[j - v[i]] 加上 v[i]。
(ps:這裏的空間是指揹包中已裝物品佔用的空間)

步驟三:得出答案ans
揹包dp[V]最小剩餘空間 = 揹包dp[V]總面積 - 揹包dp[V]最多能裝的空間。
ans = V - dp[V]

#include<bits/stdc++.h>
using namespace std;
int V, n, v[35], ans;
int dp[20000];
int main()
{
	memset(dp, 0, sizeof(dp));
	cin>>V>>n;
	for(int i=0; i<n; i++)
		cin>>v[i];
		
	for(int i=0; i<n; i++)
	{
		//將每個物品都放入不同大小的揹包
		for(int j=V; j>=v[i]; j--)
		dp[j] = max(dp[j-v[i]]+v[i], dp[j]);
	}
	
	cout<<V - dp[V];

	return 0;	
}

總結

0/1揹包問題解決方法主要是通過求小規模最優解,推到大規模最優解,難點在於狀態轉移方程的設計。
這種類型的題太常見了,每次都是用暴力求解,經常會失分,太可惜。
希望看完這篇博客的你,對0/1揹包有了更深入的理解,讓解題更加輕鬆、快樂。

一直將自己的學習經驗分享給有需要的人。
我是小鄭,一個堅持不懈的小白

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