01揹包問題_普通的遞迴
1. 問題描述:
有n個重量和價值分別為wi,vi的物品,從這些物品中挑選出總重量不超過W的物品,求所有挑選方案中價值總和的最大值。
1≤n≤100
1≤wi,vi≤100
1≤W≤10000
輸入:
n=4
(w,v)={(2,3),(1,2),(3,4),(2,2)}
W=5
輸出:
7(選擇第0,1,3號物品)
(因為對每個物品只有選和不選兩種情況,所以這個問題稱為01揹包)
2. 01揹包問題與部分揹包問題很類似,但是部分揹包問題是物品是可以進行分割的,假如不能夠拿取當前的物品,那麼可以拿取這個物品的某一部分來構成最大質量並且價值是最大的
但是01揹包問題中物品是不可以進行分割的,可以選擇拿取當前的物品也可以選擇不拿這兩種情況,所以這個是兩個問題的區別
我們先不考慮怎麼樣寫計算機程式,先是看自己的直覺思維,怎麼樣拿取物品才能夠使拿取的物品最終的價值最大但是不超過最大質量
首先我們可能考慮先湊成目標的最大質量,W = 5 那麼我們可以由多個組合來拿取,可以拿取0,1, 3號物品,0,2號物品,也可以拿取2,3號物品,再在這幾個組合中找出價值最大的,可以發現0,1, 3號物品和2,3號物品的價值是最大的
但是除了可以湊成目標最大質量情況有可能價值最大,也可能是這樣的情況:不能夠湊成最大質量,但是它的總價值有可能是最大的
我們可以拿取當前的物品,也可以不拿取,這種思維模式是不是很像求解非空集合的那到題目呢?的確兩道題目都可以這樣來想,拿或者不拿這兩種情況,只不過非空子集那道題目要記錄中間的過程,這道題目只是求解出最大價值即可
所以,我們可以使用遞迴的方式來進行解決,在所有的可能中找出一個能夠不超過目標質量但是價值是最大的,找出所有可能這實際上也是一種深度優先搜尋的過程
我們在遇到問題的時候要先進行給出測試樣例的分析,利用簡單的推導,類比,找出問題的相似性來進行問題的求解
下面是使用普通遞迴的方法來進行解決(也可以叫做深搜來解決:實際上就是深搜)
其中遞迴有兩個變數在進行變化一個是可以選擇的當前物品的下標,一個是可以選擇的當前物品的最大質量,所以在方法中傳入兩個引數來,還有其他的不變的引數也可以傳遞進來方便操作,也可以將這些設定為全域性變數不用傳遞進來都可以
3. 具體的程式碼如下:
import java.util.Scanner;
import static java.lang.Math.max;;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int w[] = new int[n];
int v[] = new int[n];
for(int i = 0; i < n; i++){
w[i] = sc.nextInt();
}
for(int i = 0; i < n; i++){
v[i] = sc.nextInt();
}
int W = sc.nextInt();
//使用普通的遞迴來解決,這裡的方法名也寫作dfs
int res = dfs(w, v, 0, n, W);
System.out.println(res);
sc.close();
}private static int dfs(int[] w, int[] v, int cur, int n, int W){
if(cur == n) return 0; //沒有東西可以拿了
//可以不使用if語句來進行限制因為後面的時候已經使用了if語句來進行剪枝當不能夠拿取物品的時候不會再繼續搜尋下去
//if(W < 0) return 0; //不可以裝東西了
int v1 = dfs(w, v, cur + 1, n, W);//剪枝(看能不拿當前的物品能夠拿才搜尋下去)
if(W >= w[cur]){
int v2 = v[cur] + dfs(w, v, cur + 1, n, W - w[cur]);
return max(v1, v2);
}else{
//當遇到比較大的質量的物品導致不能夠裝的時候直接返回當前的v1
return v1;
}
}
}
測試用例:
5
1 2 3 4 5
5 4 3 2 1
10
輸出14