題目:backpack I (每個(gè)物品取一次,求最多能裝多少物品)
Given n items with size Ai, an integer m denotes the size of a backpack. How full can you fill this backpack?
Input:
- 物品的大小:: int[]
- 背包的大小 ::int
Output:
- 背包最多能裝多少:: int
Intuition:
典型的DP問題,dp array存檔背包大小為j時(shí),dp[i]表示在裝或者不裝第 i - 1號(hào)物品時(shí)最多能裝多少物品。
- 不裝第 i 號(hào)的物品: dp[i - 1][j]
- 裝第 i 號(hào)物品: dp[i - 1][j - A[i - 1]] + A[i - 1]
舉個(gè)例子: 物品大小[3, 4, 8, 5], 背包size為10
那么在每次裝一個(gè)物品時(shí),dp array的更新如下:
Array List: 0 0 0 3 3 3 3 3 3 3 3
Array List: 0 0 0 3 4 4 4 7 7 7 7
Array List: 0 0 0 3 4 4 4 7 8 8 8
Array List: 0 0 0 3 4 5 5 7 8 9 9
Code:
TC: (nm) SC: (nm)
public int backPack(int m, int[] A) {
// write your code here
if (A == null || A.length == 0){
return 0;
}
int[][] dp = new int[A.length + 1][m + 1];
for (int i = 1; i <=A.length; i++){
for (int j = 0; j <= m; j++){
if (j >= A[i - 1]) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - A[i - 1]] + A[i - 1]);
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[A.length][m];
}
當(dāng)然,遇到二維數(shù)組的時(shí)候我們會(huì)考慮是不是要進(jìn)行空間壓縮,使用一維數(shù)組。
TC: (nm) SC: (m)
Code:
public int backPack(int m, int[] A) {
// write your code here
if (A == null || A.length == 0){
return 0;
}
int[] dp = new int[m + 1];
for (int i = 0; i <A.length; i++){
for (int j = m; j >= 0; j--){
if (j >= A[i]) {
dp[j] = Math.max(dp[j], dp[j - A[i]] + A[i]);
}
}
}
return dp[m];
}
題目:backpack II (每個(gè)物品取一次,求最多能裝物品的最大價(jià)值)
Given n items with size Ai and value Vi, and a backpack with size m. What's the maximum value can you put into the backpack?
Input:
- 物品的大小:: int[]
- 物品的價(jià)值:: int[]
- 背包的大小 ::int
Output:
- 背包能裝物品的最大價(jià)值:: int
Intuition:
基本思想和backpack I是一樣的,不同之處就在于我們更新的是物品的價(jià)值而不是物品的大小。
- 不裝第 i 號(hào)的物品: dp[i - 1][j]
- 裝第 i 號(hào)物品: dp[i - 1][j - A[i - 1]] + V[i - 1]
例子: 物品大小[2, 3, 5, 7], 物品價(jià)值[1, 5, 2, 4],背包size為10
那么在每次裝一個(gè)物品時(shí),dp array的更新如下:
Array List: 0 0 1 1 1 1 1 1 1 1 1
Array List: 0 0 1 5 5 6 6 6 6 6 6
Array List: 0 0 1 5 5 6 6 6 7 7 8
Array List: 0 0 1 5 5 6 6 6 7 7 9
Code:
TC: (nm) SC: (nm)
public int backPack(int m, int[] A) {
// write your code here
if (A == null || A.length == 0){
return 0;
}
int[][] dp = new int[A.length + 1][m + 1];
for (int i = 1; i <=A.length; i++){
for (int j = m; j >= 0; j--){
if (j >= A[i - 1]) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - A[i - 1]] + V[i - 1]);
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[A.length][m];
}
同樣的可以壓縮空間
Code:
TC: (nm) SC: (m)
public int backPackII(int m, int[] A, int[] V) {
// write your code here
// write your code here
if (A == null || A.length == 0){
return 0;
}
int[] dp = new int[m + 1];
for (int i = 0; i <A.length; i++){
for (int j = m; j >= 0; j--){
if (j >= A[i]) {
dp[j] = Math.max(dp[j], dp[j - A[i]] + V[i]);
}
}
}
return dp[m];
}
題目:backpack III (每個(gè)物品可取無(wú)限次,求最多能裝物品的最大價(jià)值)
Given n items with size Ai and value Vi, and a backpack with size m. What's the maximum value can you put into the backpack?
Input:
- 物品的大小:: int[]
- 物品的價(jià)值:: int[]
- 背包的大小 ::int
Output:
- 背包能裝物品的最大價(jià)值:: int
Intuition:
更新dp array值的策略是一樣的。不過這次更新的順序不再是從后到前,而是反過來了。這樣在后半部的更新可以利用之前的值。
例子: 物品大小[2, 3, 5, 7], 物品價(jià)值[1, 5, 2, 4],背包size為10
那么dp array的更新如下:
Array List: 0 0 1 1 2 2 3 3 4 4 5
Array List: 0 0 1 5 5 6 10 10 11 15 15
Array List: 0 0 1 5 5 6 10 10 11 15 15
Array List: 0 0 1 5 5 6 10 10 11 15 15
Code:
TC: O(nm) SC: O(m)
public int backPackIII(int[] A, int[] V, int m) {
// write your code here
// write your code here
// write your code here
if (A == null || A.length == 0){
return 0;
}
int[] dp = new int[m + 1];
for (int i = 0; i <A.length; i++){
for (int j = 0; j <= m; j++){
if (j >= A[i]) {
dp[j] = Math.max(dp[j], dp[j - A[i]] + V[i]);
}
}
}
return dp[m];
}
題目:backpack IV (每個(gè)物品可取無(wú)限次,從中找出所有的和為 target 的組合個(gè)數(shù))
Given an integer array nums with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target.
Input:
- 物品的大小:: int[]
- 目標(biāo)值 ::int
Output:
- 所有的和為目標(biāo)的組合個(gè)數(shù):: int
Intuition:
用一個(gè)dp array存0到target的可能組合數(shù),dp[i]的組合數(shù)可以根據(jù)之前組合數(shù)求得。 dp[i] += dp[i - num[i]] 或者 dp[i + num[i]] += dp[i]
Code:
TC: O(n*m) SC: O(m)
public int backPackIV(int[] nums, int target) {
// write your code here
if(nums == null || nums.length == 0) return 0;
int[] dp = new int[target + 1];
dp[0] = 1;
for(int n: nums){
for(int i = 0; i <= target; i++){
if(n + i <= target){
dp[i + n] += dp[i] ;
}
}
}
return dp[target];
}
題目:backpack V (每個(gè)物品可取一次,從中找出所有的和為 target 的組合個(gè)數(shù))
Given an integer array nums with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target.
Input:
- 物品的大小:: int[]
- 目標(biāo)值 ::int
Output:
- 所有的和為目標(biāo)的組合個(gè)數(shù):: int
Intuition:
與上一題不同,單次選擇的話更新dp array的順序應(yīng)該從后到前。
Code:
TC: O(mn) SC: O(m)
public int backPackV(int[] nums, int target) {
// write your code here
if(nums == null || nums.length == 0) return 0;
int[] dp = new int[target + 1];
dp[0] = 1;
for(int n: nums){
for(int i = target; i >= 0; i--){
if(n + i <= target){
dp[i + n] += dp[i] ;
}
}
}
return dp[target];
}
題目:backpack VI (每個(gè)物品可取無(wú)限次,數(shù)的順序不同則會(huì)被認(rèn)為是不同的組合,從中找出所有的和為 target 的組合個(gè)數(shù))
Given an integer array nums with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target.
Input:
- 物品的大小:: int[]
- 目標(biāo)值 ::int
Output:
- 所有的和為目標(biāo)的組合個(gè)數(shù):: int
Intuition:
這個(gè)題與backpackIV 不同的一點(diǎn)就是同樣的數(shù),用在組合不同的位置可認(rèn)為是有效的解。那么可以理解成,我們?cè)诿總€(gè)size裝物品的時(shí)候需要每個(gè)物品都試一遍,那么對(duì)物品size的循環(huán)就應(yīng)該成為內(nèi)循環(huán)了。
Code:
TC:O(mn) SC: O(m)
public int backPackVI(int[] nums, int target) {
// write your code here
if(nums == null || nums.length == 0) return 0;
int[] dp = new int[target + 1];
dp[0] = 1;
for(int i = 0; i <= target; i++){
for(int n: nums){
if(n + i <= target){
dp[i + n] += dp[i];
}
}
printArrayList(dp);
}
return dp[target];
}