该题我尝试使用暴力方法来求解,始终测试不通过,而后将代码改为自底向上的动态规划算法,才测试通过。
1、暴力算法(测试不通过) 暴力算法有缺陷
public int findMaxForm(String[] strs, int m, int n)
{
int len = strs.length;
int maxSum = 0; // 最大数量
int zeroNum = 0; // 零的数目
int oneNum = 0; // 一的数目
int[][] strNum = new int[len][2];
int[][] dp = new int[m+1][n+1]; // 状态矩阵
for (int k=0; k<len; k++)
{
zeroNum = 0; // 零的数目
oneNum = 0; // 一的数目
for (int i=0; i<strs[k].length(); i++)
{
if (strs[k].charAt(i) == '0')
zeroNum++;
else
oneNum++;
}
// 存储0、1个数
strNum[k][0] = zeroNum;
strNum[k][1] = oneNum;
}
return solve(m, n, strNum, 0);
}
// 暴力问题 m个0,n个1, 这里m和n两个变量 (错误代码)
public int solve(int m, int n, int[][] strNum, int idx)
{
if (idx == strNum.length)
return 0;
if (m < 0 || n < 0)
return 0;
return Math.max(solve(m-strNum[idx][0], n-strNum[idx][1], strNum, idx+1)+1,
solve(m, n, strNum, idx+1));
}
2、动态规划
public int findMaxForm(String[] strs, int m, int n)
{
int len = strs.length;
int maxSum = 0; // 最大数量
int zeroNum = 0; // 零的数目
int oneNum = 0; // 一的数目
int[][] strNum = new int[len+1][2];
int[][][] dp = new int[len+1][m+1][n+1]; // 状态矩阵
for (int k=0; k<len; k++)
{
zeroNum = 0; // 零的数目
oneNum = 0; // 一的数目
for (int i=0; i<strs[k].length(); i++)
{
if (strs[k].charAt(i) == '0')
zeroNum++;
else
oneNum++;
}
// 存储0、1个数
strNum[k+1][0] = zeroNum;
strNum[k+1][1] = oneNum;
}
// 开始计算
for (int i=0; i<=len; i++) // 第1个到第len个字符串
{
for (int j=0; j<=m; j++) // 0的范围
{
for (int k=0; k<=n; k++) // 1的范围
{
if (i == 0)
dp[i][j][k] = 0; // 先把已知的条件填充好(字符数组中一个字符串也没有时)
else if (j >= strNum[i][0] && k >= strNum[i][1])
dp[i][j][k] = Math.max(dp[i-1][j][k], dp[i-1][j-strNum[i][0]][k-strNum[i][1]] + 1);
else
dp[i][j][k] = dp[i-1][j][k]; // 表示还没有计算第i个字符串时
}
}
}
return dp[len][m][n];
}
优化后:
public int findMaxForm(String[] strs, int m, int n)
{
int len = strs.length;
int maxSum = 0; // 最大数量
int zeroNum = 0; // 零的数目
int oneNum = 0; // 一的数目
int[][] strNum = new int[len][2];
int[][] dp = new int[m+1][n+1]; // 状态矩阵
for (int k=0; k<len; k++)
{
zeroNum = 0; // 零的数目
oneNum = 0; // 一的数目
for (int i=0; i<strs[k].length(); i++)
{
if (strs[k].charAt(i) == '0')
zeroNum++;
else
oneNum++;
}
// 存储0、1个数
strNum[k][0] = zeroNum;
strNum[k][1] = oneNum;
}
// 开始计算(降维)
for(int[] num: strNum)
{
for (int i=m; i>=num[0]; --i)
{
for (int j=n; j>=num[1]; --j)
{
dp[i][j] = Math.max(dp[i][j], 1+dp[i-num[0]][j-num[1]]);
}
}
}
return dp[m][n];
}
参考:https://leetcode.com/problems/ones-and-zeroes/discuss/95807/0-1-knapsack-detailed-explanation.