879 盈利计划
思路
三维背包问题。三个维度分别表示几份、容量限制1、容量限制2。
状态转移方程为
{
d
p
[
i
]
[
j
]
[
k
]
=
d
p
[
i
−
1
]
[
j
]
[
k
]
,
j
<
g
r
o
u
p
[
i
−
1
]
d
p
[
i
]
[
j
]
[
k
]
=
(
d
p
[
i
−
1
]
[
j
]
[
k
]
+
d
p
[
i
−
1
]
[
j
−
g
r
o
u
p
[
i
−
1
]
]
[
M
a
t
h
.
m
a
x
(
0
,
k
−
p
r
o
f
i
t
[
i
−
1
]
)
]
)
%
m
o
d
,
o
t
h
e
r
s
\left \{ \begin{aligned} &dp[i][j][k] = dp[i-1][j][k],&j<group[i-1] \\ &dp [i][j][k] = (dp[i-1][j][k] + dp[i-1][j-group[i-1]][Math.max(0,k-profit[i-1])]) \% mod, &others \end{aligned} \right.
{dp[i][j][k]=dp[i−1][j][k],dp[i][j][k]=(dp[i−1][j][k]+dp[i−1][j−group[i−1]][Math.max(0,k−profit[i−1])])%mod,j<group[i−1]others
因此得到如下代码,详细解释见代码注释。
代码
public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {
int len = group.length;
int mod = (int)1e9+7;
//三维分别表示可供选择的工作数量(可选择,也就是对于某些工作可做可不做)、投入多少人(不一定全部用光)、至少可以实现多少利润(大于等于0)
int[] [] [] dp = new int[len+1][n+1][minProfit+1];
//处理边界条件
dp[0][0][0] = 1;
for (int i=1;i<=len;i++){
for (int j = 0;j<=n;j++) {
for (int k=0;k<=minProfit;k++) {
if (j<group[i-1])//如果当前的人数不能做第i份工作,则方案数等于上一份工作的方案数
dp[i][j][k] = dp[i-1][j][k];
else//如果可以做第i份工作,则当前工作的方案数等于上一份工作的方案数+上一份工作中满足人数不超过j-group[i-1](表示多份工作可以一起做)、且利润不少于0的方案数量(利润等于0表示只做了当前工作,大于0表示除了当前工作还做了其他工作)
dp[i][j][k] = (dp[i-1][j][k] + dp[i-1][j-group[i-1]][Math.max(0,k-profit[i-1])]) % mod;
}
}
}
int sum = 0;
//统计满足条件的方案数量即在len份工作中,利润不少于miniProfit的方案数量
for (int j=0;j<=n;j++){
sum = (sum+dp[len][j][minProfit])%mod;
}
return sum;
}
1049. 最后一块石头的重量 II
思路
可以化简为二维背包问题。两个维度分别表示几块、容量限制。详细解释见代码注释。
代码
public int lastStoneWeightII(int[] stones) {
int len = stones.length;
//借助目标和的思想,因为要求能达到的剩余石块的最小重量,所以可以把石头分为两堆,使用一堆的总重量减去另一堆的重量即为剩余的重量,
//这里把两个堆设为A堆和B堆,其中A堆的重量大于等于B堆的重量
//为了得到最小的剩余石头重量,两个堆的总重量应该尽可能的接近,最佳情况是相等。
//此处首先获得最佳的情况(因为石料不可分割,所以这个值可能达不到,但这个是最佳情况)
int sum = 0;
for (int i: stones)
sum += i;
int t = sum / 2;
//定义数组,第一个元素指的是参与计算的石块数量,第二个元素指的是当前条件下B堆可以达到的最大重量,如果可以达到,则是true,否则是false
boolean [][] dp = new boolean[len+1][t+1];
dp[0][0] = true;
//从1块石头参与计算开始进行动态规划
for (int i=1;i<=len;i++)
{
for (int j=0;j<=t;j++)
{
//如果当前B堆的最大重量大于当前石头的重量,存在两种情况-选或不选,选的话-dp[i-1][j-stones[i-1],不选-dp[i-1][j]
//因为此时判断的是能不能达到j,所以两种情况有一个可以达到即为可以达到
if (j>=stones[i-1])
dp[i][j] = dp[i-1][j] || dp[i-1][j-stones[i-1]];
else
dp[i][j] = dp[i-1][j];
}
}
// for (int i = 0;i<=len;i++)
// {
// for (int j=0;j<=t;j++)
// System.out.print("\t"+dp[i][j] + " ");
// System.out.println();
// }
//因为最后求的是B堆可以达到的最大重量,所以从理想情况开始向下遍历,寻找满足条件的最大值
for (int j=t;j>=0;j--)
{
if (dp[len][j])
return sum-2*j;
}
return 0;
}
474. 一和零
思路
同样是三维背包问题。三个维度分别表示几份、容量限制1、容量限制2。
状态转移方程为
{
d
p
[
i
]
[
j
]
[
k
]
=
d
p
[
i
−
1
]
[
j
]
[
k
]
,
o
t
h
e
r
s
d
p
[
i
]
[
j
]
[
k
]
=
M
a
t
h
.
m
a
x
(
d
p
[
i
]
[
j
]
[
k
]
,
d
p
[
i
−
1
]
[
j
−
c
[
0
]
]
[
k
−
c
[
1
]
]
)
,
j
<
=
c
[
0
]
&
&
k
<
=
c
[
1
]
\left \{ \begin{aligned} &dp[i][j][k] = dp[i-1][j][k],&others \\ &dp [i][j][k] =Math.max (dp[i][j][k] ,dp[i-1][j-c[0]][k-c[1]]), &j<=c[0] \&\& k<=c[1]\end{aligned} \right.
{dp[i][j][k]=dp[i−1][j][k],dp[i][j][k]=Math.max(dp[i][j][k],dp[i−1][j−c[0]][k−c[1]]),othersj<=c[0]&&k<=c[1]
因此得到如下代码。
代码
public int findMaxForm(String[] strs, int m, int n) {
int len = strs.length;
//最后判断满足条件的最大元素个数
int[][][] dp = new int[len+1][m+1][n+1];
for (int i=1;i<=len;i++)
{
int[] c = count_num(strs[i-1]);
for (int j=0;j<=m;j++)
{
for (int k=0;k<=n;k++)
{
dp[i][j][k] = dp[i-1][j][k];
if (c[0]<=j && c[1]<=k)
dp[i][j][k] = Math.max(dp[i][j][k], dp[i-1][j-c[0]][k-c[1]]+1);
}
}
}
return dp[len][m][n];
}
public int[] count_num(String str)
{
int[] a = new int[2];
Arrays.fill(a,0);
for (char ch : str.toCharArray())
{
if (ch == '1')
a[1]++;
else
a[0]++;
}
return a;
}