java實現最通俗易懂的01背包問題


這幾天一直在想背包問題,昨天還有個學長專門講了,但是還是不是很理解,今天我終於想通了背包問題,其實只要理解了這個思路,不管用什么語言,肯定是能編出來的。下面我就來介紹一下背包問題。

1.題目描述:

有如下5種物品,小明的書包最多只能裝下8公斤的物品,小明特別貪心,思考怎么選擇使自己書包能裝下並且得到的價值最大。

物品1:6公斤     價值48元
物品2:1公斤     價值7元
物品3:5公斤     價值40元
物品4:2公斤     價值12元
物品5:1公斤     價值8元

2.解題思路:

其實我們正常思維,一般會想,我要盡量裝滿8公斤,把最大的價值求出來,可是這樣的話,就需要用到遞歸,遞歸能解出來,只是算法難度高。但是什么是動態規划,動態規划就是逆着來,我要求裝8公斤的物品怎么裝使得價值最大。我可以先考慮裝0公斤,最大價值,再考慮裝1公斤最大價值,考慮裝2公斤最大價值,裝3公斤最大價值,把前面都記錄下來。用另外一個temp[i][j]數組記錄下來。i呢表示我現在出現的物品的數量,當i循環到最后一個數量的時候就結束了嘛。自己想象一下,我雖然有5件物品,先只給你一件,你判斷能不能裝下,能裝下,那么你就看你裝下這件物品,和不裝下這件物品哪個價值高,那么記錄下來即可。具體填下下面的表試下,真正會填表就差不多了

      0  1  2  3  4  5   6    7    8
      0  0  0  0  0  0   0    0    0
6 48  0  0  0  0  0  0   48  48   48
1 7   0  7  7  7  7  7   48  55   55
5 40  0  7  7  7  7  40  48  55   55
2 12  0  7  12 19 19 40  48  55   60
1 8   0  8  15 20 27 40  48  56   63 

 

這個表是怎么來的呢?,舉個例子,當我橫着一排一排填,因為最開始只掉一件物品,開始掉重量是6的物品,到6的時候就能裝下了,所以temp[1][6]=48,另外7,8也只有這一件物品填。所以還是一樣的。

接下來掉第二個物品,掉下1這個物品了,1的價值是7,也就是temp[2][1] = 7。后面一直到temp[2][5]都是等於7。 重點重點來了,到了temp[2][6]的時候怎么辦呢?我們的當然一看就是,我要選第一件物品,它的價值是48,可是選了第一件物品,背包就滿了,就不能裝第二件重量為1的物品了,所以temp[2][6] = 48。

我們人可以這樣思考,關鍵就來了,關鍵就是怎么讓計算機也可以這樣思考呢,我們就需要用代碼。其實仔細想想,我掉第二件物品了,判斷如果不能裝下,那么temp[2][j] = temp[2][j-1] //記住j表示的是我背包現在最大只能裝j這么多,那既然這個物品裝不下,那么當然不能裝了。如果我當前物品重量小於j,那么我就可以選擇是裝還是不裝呢?只要比較裝還是不裝哪個價值大就行了。如果不裝的話價值這個時候的最大價值是不會變的,因為都不裝嘛,也就是,temp[2][j] = temp[2-1][j] 。如果我裝的話,關鍵是這個,還是到剛才的到第二件物品的價值為6的時候考慮,如果我裝下重量1這個物品,temp[2][6] = temp[2-1][6 - 這個物品的重量] + 物品的價值  //

其實我   temp[2-1][6-這個物品重量]表示我還沒裝這個物品之前的價值騰出裝這個物品的空間,然后加上這個物品的價值進行比較。關鍵是之前的每一個狀態都用數組給記錄下來了。關鍵代碼先看一下,結合代碼再理解

		for(int i=1;i<6;i++) { 
			for(int j=1;j<9;j++) {
				if(w[i]<=j) {
					temp[i][j] = Math.max(temp[i-1][j], temp[i-1][j-w[i]]+v[i]); //其實就是比較物品選還是不選哪種價值大。
				}else {
					temp[i][j] = temp[i-1][j];//第i件物品不能放
				}
			}
		}

 這個理解了,相信你自己也能編出這段代碼來了。

3.代碼示例

package 背包問題;

import java.util.Scanner;

/*
 * 
最多可以放8公斤的物品
物品1:6公斤   價值48元
物品2:1公斤   價值7元
物品3:5公斤   價值40元
物品4:2公斤   價值12元
物品5:1公斤   價值8元

      0  1  2  3  4  5   6    7    8
      0  0  0  0  0  0   0    0    0
6 48  0  0  0  0  0  0   48  48   48
1 7   0  7  7  7  7  7   48  55   55
5 40  0  7  7  7  7  40  48  55   55
2 12  0  7  12 19 19 40  48  55   60
1 8   0  8  15 20 27 40  48  56   63 
	   
 */
public class Test01 {
	public static void main(String[] args) {
		//定義一個數組a[i][j] = a[i]                 i表示當前物品的序號選上,j表示這個位置能得到的最大值
		//選或者不選動態規划
		Scanner scn = new Scanner(System.in);
		int [] w = new int[6];//表示每件物品的重量
		int [] v = new int[6];//表示每件物品的價值
		for(int i=1;i<6;i++) {
			w[i] = scn.nextInt();//輸入重量
			v[i] = scn.nextInt();//輸入價值
		}
		
		int [][] temp = new int[6][9];   //8表示背包最多能放8公斤的重量
		for(int j=0;j<9;j++) {  //初始化每一行
			temp[0][j] = 0;
		}
		for(int i=1;i<6;i++) {  //背包的重量為0的時候,最大價值肯定是0
			temp[i][0] = 0;
		}
		
		for(int i=1;i<6;i++) {  //從第一個物品開始選,記錄我選了前面出現的物品,背包重量從1-8的能選上的最大的值
			for(int j=1;j<9;j++) { //當i循環到最后一層5的時候,也就是得到了,我5件物品都選上的時候的最大的值
				if(w[i]<=j) { //重量比這個狀態小,那么就能放。 那么就只是放與不放,看是放重量大,還是不放重量大
					temp[i][j] = Math.max(temp[i-1][j], temp[i-1][j-w[i]]+v[i]);
				}else {
					temp[i][j] = temp[i-1][j];//第i件物品不能放
				}
			}
		}
		for(int i=0;i<6;i++) {
			for(int j=0;j<9;j++) {
				System.out.printf("%-5d",temp[i][j]);
			}
			System.out.println();
		}
	}
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM