LeetCode 一和零(动态规划)

在计算机界中,我们总是追求用有限的资源获取最大的收益。

现在,假设你分别支配着 m 个 0 和 n 个 1。另外,还有一个仅包含 0 和 1 字符串的数组。

你的任务是使用给定的 m 个 0 和 n 个 1 ,找到能拼出存在于数组中的字符串的最大数量。每个 0 和 1 至多被使用一次。

注意:

给定 0 和 1 的数量都不会超过 100。
给定字符串数组的长度不会超过 600。

示例 1:

输入: Array = {"10", "0001", "111001", "1", "0"}, m = 5, n = 3
输出: 4
解释: 总共 4 个字符串可以通过 5 个 0 和 3 个 1 拼出,即 "10","0001","1","0" 。

示例 2:

输入: Array = {"10", "0", "1"}, m = 1, n = 1
输出: 2
解释: 你可以拼出 "10",但之后就没有剩余数字了。更好的选择是拼出 "0" 和 "1" 。

思路分析:每一个字符串都可以放入或者不放入,放入和不放入都会对后面的选择有影响,所以这是一道典型的动态规划题。

class Solution {
public:
	int findMaxForm(vector<string>& strs, int m, int n) {
		int strsSize = strs.size(), maxRes = 0;
        if (strsSize == 0){
            return maxRes;
        }
		vector<int> myStrVec;//myStrVec[i]用于统计strs[i]字符串中0、1的个数,(0的个数 * 1000 + 1的个数) (因为0,1个数不会超过100
		for (auto &str : strs) {
			int zeroNum = 0, oneNum = 0;
			//统计str串中,0,1的个数
			for (auto ch : str) {
				if (ch == '0') {
					zeroNum += 1;
				}
				else {
					oneNum += 1;
				}
			}
			//组合放入myStrVec
			myStrVec.push_back(zeroNum * 1000 + oneNum);
		}
		vector<pair<int, int> > dp(strsSize, { m * 1000 + n, 0 });//dp[i]用于记录放入第i个字符串可得到的中间最大结果
		for (int i = 0; i < strsSize; ++i) {
			int maxIndex = -1, tempRes = 0;//放入i时,j的最优下标
			for (int j = 0; j < i; ++j) {
                //如果j中剩余的个数能够放入i,并且放入之后会大于之前的结果
				if (dp[j].first / 1000 >= myStrVec[i] / 1000 && dp[j].first % 1000 >= myStrVec[i] % 1000 && dp[j].second + 1 > tempRes) {
					maxIndex = j;
					tempRes = dp[j].second + 1;
				}
			}
            //更新是否放入i
			if (maxIndex != -1) {
                //将i放入maxIndex中达到最大值
				dp[i].first = (dp[maxIndex].first / 1000 - myStrVec[i] / 1000) * 1000 + (dp[maxIndex].first % 1000 - myStrVec[i] % 1000);
				dp[i].second = tempRes;
			}
            else if (m >= myStrVec[i] / 1000 && n >= myStrVec[i] % 1000){
                //否则考虑初始值放入i
                dp[0].first = (m - myStrVec[i] / 1000) * 1000 + (n - myStrVec[i] % 1000);
                dp[0].second = 1;
            }
            maxRes = max(maxRes, tempRes);
		}
		return maxRes;
	}
};

在这里插入图片描述
上面的方法存在一个逻辑漏洞,因为无法保证放入i的时候让剩余的0、1的个数尽量的多,导致求出的可能是局部最优解,但是最终结果不是整体最优解。
方法:其实这是一个二维的背包问题,即将0,1的进行二维处理。

class Solution {
public:
	int findMaxForm(vector<string>& strs, int m, int n) {
		int strsSize = strs.size();
		vector<int> myStrVec;//myStrVec[i]用于统计strs[i]字符串中0、1的个数,(0的个数 * 1000 + 1的个数) (因为0,1个数不会超过100
		for (auto &str : strs) {
			int zeroNum = 0, oneNum = 0;
			//统计str串中,0,1的个数
			for (auto ch : str) {
				if (ch == '0') {
					zeroNum += 1;
				}
				else {
					oneNum += 1;
				}
			}
			//组合放入myStrVec
			myStrVec.push_back(zeroNum * 1000 + oneNum);
		}
		vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));//dp[i][j]用于记录i个0,j个1的最优解
        for (int index = 0; index < strsSize; ++index){//穷举所有字符串是否放入
            for (int i = m; i >= myStrVec[index] / 1000; --i){//0的个数
                for (int j = n; j >= myStrVec[index] % 1000; --j){//1的个数
                    dp[i][j] = max(dp[i][j], dp[i - myStrVec[index] / 1000][j - myStrVec[index] % 1000] + 1);
                }
            }
        }
        return dp[m][n];
	}
};

在这里插入图片描述
由于上面的算法多次计算myStrVec[index] / 1000,myStrVec[index] % 1000,可以使用变量保存。

class Solution {
public:
	int findMaxForm(vector<string>& strs, int m, int n) {
		int strsSize = strs.size();
		vector<int> myStrVec;//myStrVec[i]用于统计strs[i]字符串中0、1的个数,(0的个数 * 1000 + 1的个数) (因为0,1个数不会超过100
		for (auto &str : strs) {
			int zeroNum = 0, oneNum = 0;
			//统计str串中,0,1的个数
			for (auto ch : str) {
				if (ch == '0') {
					zeroNum += 1;
				}
				else {
					oneNum += 1;
				}
			}
			//组合放入myStrVec
			myStrVec.push_back(zeroNum * 1000 + oneNum);
		}
		vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));//dp[i][j]用于记录i个0,j个1的最优解
        for (int index = 0; index < strsSize; ++index){
            //str[index]需要的0的个数needM,str[index]需要的1的个数needN
            int needM = myStrVec[index] / 1000, needN = myStrVec[index] % 1000;
            for (int i = m; i >= needM; --i){
                for (int j = n; j >= needN; --j){
                    dp[i][j] = max(dp[i][j], dp[i - needM][j - needN] + 1);
                }
            }
        }
        return dp[m][n];
	}
};

这样更方便理解。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_41855420/article/details/88963333