从栈中取出 K 个硬币的最大面值和:
思路:
第一步:该题类似于背包问题,将不同的栈看作不同组背包,背包里有不同的商品。根据多重背包,将一个栈内的相邻元素打包做一个商品(也就是将求栈的前缀和)于是转化成为一个普通的01背包问题:
eg: 将第一组数据 [[1,100,3],[7,8,9]] 的 硬币打包。
第
一
组
背
包
硬
币
:
[
1
,
100
,
3
]
=
=
>
[
1
,
101
,
104
]
第
二
组
背
包
硬
币
:
[
7
,
8
,
9
]
=
=
>
[
7
,
15
,
24
]
第一组背包硬币:[1,100,3] == > [1,101,104] 第二组背包硬币:[7,8,9] ==>[7,15,24]
第一组背包硬币:[1,100,3]==>[1,101,104]第二组背包硬币:[7,8,9]==>[7,15,24]
第二步:显而易见,现在k相当于就是限制背包容量的参数,也就是 硬币数不超过k。
第三步:状态转移,第i组背包的第j个空间的值就等于 第i-1组背包的第j-m位置上的最大值 加上空间占用为m的物品 与 当前位置的最大值。
j为当前背包空间,i为第i组背包,sum[m]为第m个商品的值,m为占用的空间
状态转移方程:
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
j
−
m
]
+
s
u
m
[
m
]
,
f
[
i
]
[
j
]
)
f[i][j] = max(f[i-1][j-m]+sum[m],f[i][j])
f[i][j]=max(f[i−1][j−m]+sum[m],f[i][j])
public int maxValueOfCoins(List<List<Integer>> piles, int k) {
int[][] f = new int[f.size()+1][k+1];
for (int i = 1; i <= piles.size(); i++) {
List<Integer> l= l.get(i-1);
int max= Math.min(k,l.size());
int[] sum = new int[max+1];
sum[0]=0;
for(int j = 1; j <= length ; j++){
sum[j] = sum[j-1]+l.get(j-1);
//System.out.println(sum[j]);
}
for (int j = 1; j <= k; j++) { // 从空间的地方开始枚举适合的商品
for (int l = 0; l <= Math.min(j, l.size()); l++) { // 注意 这里是从占用空间为0的地方开始枚举,这样可以转移前面一组的空间为1的值
dp[i][j]=Math.max(f[i][j],f[i-1][j-l]+sum[l]); // 状态转移
}
}
}
return f[piles.size()][k];
}
优化:
当我们从空间为1处枚举到空间为k处,存在冗余的枚举。可以类似于01背包的优化:从空间位置最大的地方开始枚举每件物品
class Solution {
public int maxValueOfCoins(List<List<Integer>> piles, int k) {
int[][] f = new int[piles.size()+1][k+1];
for(int i = 1 ; i <= piles.size();i++){
List<Integer> l = piles.get(i-1);
int length = Math.min(l.size(),k);
int[] sum = new int[length+1];
for(int j = 1; j <= length ; j++){
sum[j] = sum[j-1]+l.get(j-1);
//System.out.println(sum[j]);
}
for(int m = 0 ; m <= length;m++){ // 第m个物品
for(int j = k ; j>= m ; j--){ // 当空间j小于m时就换到下一个商品
// System.out.println(j + " : " + m+" : min "+Math.min(piles.size(),j));
f[i][j] = Math.max(f[i][j],f[i-1][j-m]+sum[m]); // 将背包里的物品更新
}
}
}
return f[piles.size()][k];
}
}