寫在最前面, 這些方法肯定不是我原創的,也是看的別人的思路,
然后自己理解并且實現了
https://blog.csdn.net/iihtd/article/details/51611810?utm_source=blogxgwz2
這個里面講了背包問題,以及背包的衍生問題,還是不錯的,
https://blog.csdn.net/stack_queue/article/details/53544109
這個是著名的背包九講,講的很好
0-1 背包問題:
0-1 背包問題解決的是物品只能夠選擇1次要不就不選,在背包能夠裝得下的前提下面,能夠保證裝的價值最大:
狀態轉移方程 f[i][j] = max{ f[i-1][j] , f[i-1][j - wi] +vi }; 0≤j - wi≤j
用二維的空間來表示是比較簡單的
一維表示:f[v] 表示 f[i][v] , 那么由于上面的這個公式: f[i][j] = max{ f[i-1][j] , f[i-1][j - wi] +vi };
可以發現f[i][j] 的值只是和f[j-1][j] , 和f[i-1][j-wi] ,如果我們寫出如下的代碼:
for(int i = 0 ; i < n ; i++){
for(int j = m ; j >=0 ; j--){
vec[j] = max(vec[j] , vec[j-w[i]]+v[i] );
}
}
那么對于i = N 的情況,我們計算 vec [j] 時, 由于j-wi < j ,那么vec[j-w[i]]的值會在vec[j]后參與計算,所以,在計算vec[j ] 的時候,我們先讀取到的vec[j] 實際上是vec[N-1][j] , 讀取到的 vec[j-w[i]]實際上是vec[N-1][j-w[i]],所以可以用上面的公式進行簡化。
現在是0-1背包問題的二維代碼實現:
/**
int[] w 表示物品的重量數組
int[] v 表示物品的價值
int weight 表示背包能夠裝入的重量
*/
public static int backpack01(int[] w, int[] v, int weight) {
int n = w.length + 1; //多少個物品
int[][] temp = new int[n][weight + 1];
for (int i = 0; i < n; i++) {
temp[i][0] = 0;//代表背包承受0重量時,背包的價值
}
for (int j = 0; j < weight + 1; j++) {
temp[0][j] = 0;//代表背包存放0個物品時,背包的價值
}
for (int i = 1; i < n; i++) {
for (int j = 1; j < weight + 1; j++) {
if (j > w[i]) { //這樣背包才能放下這個物品
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];
}
}
}
return temp[n-1][weight];
}
現在是0-1背包問題的一維代碼實現
當只有第一個物品的時候,相當于是往背包里面裝第一個東西,
第二遍是有兩個物品的時候了,相當于在有第一個物品的基礎上,
放第二個物品來使背包價值最大化
/**
int[] w 表示物品的重量數組
int[] v 表示物品的價值
int weight 表示背包能夠裝入的重量
*/
public static int backpack01youhua(int[] w, int[] v, int weight) {
int[] f = new int[weight + 1];
for (int i = 0; i < v.length; i++) {
for (int j = weight; j >= w[i]; j--) {
f[j] = Math.max(f[j], f[j - w[i]] + v[i]);
}
}
return f[weight];
}
完全背包問題
完全背包問題中每個物品都可以選擇無限多次
for (int j = 0; j <=weight; j++)
所以這個地方不同于01背包從背包里面扔東西,
這個是往背包里面一步一步的加
/**
int[] w 表示物品的重量數組
int[] v 表示物品的價值
int weight 表示背包能夠裝入的重量
*/
public static int completebackpack(int[] w, int[] v, int weight) {
int[] f = new int[weight + 1];
for (int i = 0; i < v.length; i++) {
for (int j = 0; j <=weight; j++) {
f[j] = Math.max(f[j], f[j - w[i]] + v[i]);
}
}
return f[weight];
}
多重背包問題
部分物品的數量有限,那就和01背包問題一樣
/**
int[] w 表示物品的重量數組
int[] v 表示物品的價值
int[] num 表示每個物品的數量
int weight 表示背包能夠裝入的重量
*/
public static int completebackpack(int[] w, int[] v, int[] num, int weight) {
int[] f = new int[weight + 1];
for (int i = 0; i < v.length; i++) {
for (int j = weight; j >= w[i]; j--) {
for (int k = 1; k <= num[i]; k++) {
if (j - k * w[i] > 0 && f[j - k * w[i]] + k * w[i] > f[j]) {
f[j] = f[j - k * w[i]] + k * w[i];
}
}
}
}
return f[weight];
}
背包衍生問題: 硬幣湊整數 即子集合加總問題
硬幣問題和完全背包問題類似,原題是這樣的:有1分,2分,5分,10分四種硬幣,每種硬幣數量無限,給定n分錢,有多少中組合可以組成n分錢?
采用動態規劃的思路,f(i,j)表示的是可以取的0,1,2...i種硬幣的情況下,湊夠j元一共有多少種方法.遞推公式以及相應的一維如何推導為二維如下:
/**
int[] coins 代表有多少種類型的coin
int target 代表要湊成的數目
*/
public static int coins(int[] coins, int target) {
int n = coins.length;
int[] f = new int[target + 1];
f[0] = 1;
for (int i = 0; i < n; i++) {
for (int j = 0; j <= target; j++) {
f[j] = f[j] + f[j - coins[i]];
}
}
return f[target];
}
附加一個衍生問題
https://www.cnblogs.com/wudongyang/p/4949557.html
0-1背包問題與子集合加總問題的近似算法
組合總和IIIhttps://blog.csdn.net/wjoker/article/details/84404478
輸入兩個整數值n和m,求出整數1到n之間的和為m的所有組合
https://blog.csdn.net/qq_36653505/article/details/82634774 python版本簡單, java版本麻煩一些
https://blog.csdn.net/weixin_37365120/article/details/70068214
給定一個數組,從中查找是否存在兩個數的和等于一個給定的x
另外借鑒了人家一種o(n)的方法,借用一個容器,在容器中查找x-arr[n]的值是否存在,存在就返回 true,不存在將這個值加入容器。
https://blog.csdn.net/chen3749102/article/details/45336371
public static boolean hasAB(int[] arr, int x){
HashSet<Integer> set=new HashSet<>();
for(int i=0;i<arr.length;i++){
if(set.contains(x-arr[i]))
return true;
else
set.add(arr[i]);
}
return false;