比較經典的一個問題,在理清思路之後,寫出了代碼。動態規劃的思想還是將大的問題分解成小的的問題。或者說是分成小的步驟。例如在揹包問題中,想求解揹包容量爲N的最大價值,先求解容量爲N-1的最大價值,一步一步向前推,最終在返回來。自己也是理解了好久,突然有一天早上刷牙,就把問題想明白了。還是要多悟,多說無益,看代碼:
package com.zxg.algorithm.PackageQuestion;
/**
* 01揹包問題 將重量爲(w1,w2,w3,w4,w5...)、價值爲(v1,v2,v3,v4,v5...)的物品放入容量爲N的揹包中,怎樣放價值最大
* 動態規劃:
* 我們設v[i][j]爲放入前i個物品到容量爲j的揹包中的最大值,i一定是小於物品總個數的,j一定是小於N的
* 舉個例子 v[2][5]表示將前兩個物品放入容量爲5的揹包中的最大價值 v[2][6]表示將前兩個物品放入容量爲6的揹包中的最大價值
* 則有等式:
* v[0][j] = v[j][0] = 0
* v[i][j] = v[i-1][j] w[i]>j
* v[i][j] = Max(v[i-1][j],v[i-1][j-w[i]]+value[i]) w[i] <= j
* 暴力求解:
* 求出物品個數的所有子集,計算每一個子集的價值,選出最大值
* 例如有4個物品,那麼2^4=16個子集,每個子集有四位,每一位是0代表該子集中沒有該物品,是1代表有該物品
* 0 0 0 0 代表該子集中沒有任何物品
* 0 1 0 1 代表該子集中有2號物品和4號物品
*/
public class Package_01 {
/**
* 動態規劃
* @param weight
* @param value
* @param capacity
* @return
*/
public static int maxValue(int[] weight, int[] value, int capacity) {
int weightLen = weight.length;
int valueLen = capacity + 1;//列值長度加1,是因爲最後一列要保證重量值爲lenColumn
int maxValue = 0;
int[][] v = new int[weightLen][valueLen];
for (int i = 0; i < weightLen; i++) {
for (int j = 0; j < valueLen; j++) {
if (i == 0) {
v[i][j] = 0;
} else if (j == 0) {
v[i][j] = 0;
} else {
if (weight[i] > j) {
v[i][j] = v[i - 1][j];
} else if (weight[i] <= j) {
v[i][j] = Math.max(v[i - 1][j], v[i - 1][j - weight[i]] + value[i]);
}
maxValue = v[i][j];
}
}
}
return maxValue;
}
/**
* 暴力求解
* @param weight
* @param value
* @param capacity
* @return
*/
public static int maxValueForce(int[] weight,int[] value,int capacity){
//子集個數
int row = (int)Math.pow(2,weight.length);
//每個子集中元素個數,就是物品的個數
int column = weight.length;
//物品子集
int[][] goodsSubset = new int[row][column];
//最大價值
int maxValue = 0;
//填充所有子集
for(int i=0;i<row;i++){
int temp_1 = i;
for(int j=0;j<column;j++){
int temp_2 = temp_1%2;
goodsSubset[i][j] = temp_2;
temp_1 = temp_1/2;
}
}
//遍歷子集,爲每一個子集計算總價值,輸出總價值最大的子集
for(int i=0;i<goodsSubset.length;i++){
int tempWeight = 0;
int tempValue = 0;
for(int j=0;j<goodsSubset[i].length;j++){
System.out.printf(goodsSubset[i][j]+" ");
tempWeight += goodsSubset[i][j]*weight[j];
tempValue += goodsSubset[i][j]*value[j];
}
System.out.print("\t"+"總重量爲:"+ tempWeight);
if(tempWeight <= capacity){
System.out.printf("\t"+"總價值爲:"+tempValue);
}else {
System.out.printf("\t"+"不可行,超出揹包最大承重");
}
if(tempWeight <= capacity && tempValue>maxValue){
maxValue = tempValue;
}
System.out.println();
}
System.out.println("最大值:"+maxValue);
return maxValue;
}
public static void main(String[] args) {
// int[] weight = {1, 3, 5, 7, 1};
// int[] value = {2, 4, 3, 6, 3};
int[] weight = {7,3,4,5};
int[] value = {42,12,40,25};
int capacity = 10; //容量
System.out.println(maxValue(weight, value, capacity));
}
}
參考:https://www.cnblogs.com/liuzhen1995/p/6374541.html#a2.3
https://blog.csdn.net/ls5718/article/details/52227908
https://www.cnblogs.com/Christal-R/p/Dynamic_programming.html
這裏再多說一句,爲什麼不能使用貪心算法思路來解決,因爲使用貪心算法的前提是無後效性,即某個狀態以後的過程不會影響以前的狀態,只與當前狀態有關。但是放在該問題中不適用,因爲你在當前選擇了一個物品,就增加了揹包相應的重量,那麼剩餘的重量就會減少,這對之後再選擇物品時時有影響的。 所以對所採用的貪心策略一定要仔細分析其是否滿足無後效性。