前言:這道題可以說是最經典的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揹包有了更深入的理解,讓解題更加輕鬆、快樂。
一直將自己的學習經驗分享給有需要的人。
我是小鄭,一個堅持不懈的小白