C++ 将二进制字符串拆分为K个子集,使得0和1的出现次数乘积之和最小
二进制字符串由一连串的二进制数字组成,即0或1。它是一种编码数据的方法,只使用两个数字,适用于存储和处理数据的计算机应用程序,这些应用程序使用只能识别两种状态的电子电路。在计算机编程中,二进制字符串经常用于表示数据,例如数字、文本和图像,这样电子电路才能方便处理。
方法
方法1. 动态规划
方法2. 贪心法
方法1:动态规划
为了解决这个问题,我们可以使用动态规划。设dp[i][j]
表示二进制字符串的前i个字符分成j个子集的最小乘积之和。通过测试所有可能的分割点k(1≤k≤i),然后计算dp[i][j]
,我们可以确定dp[k][j-1]+cost[k+1][i]
的最小值,其中cost[k+1][i]
是将子串从k+1到i分割的代价,即子串中0和1的数量分别乘以2。
语法
下面提供动态规划方法的语法−
- 创建一个名为dp的二维表,尺寸为K+1行乘以n+1列,其中K是子集的数量,n是二进制字符串的长度。当二进制字符串的前j个字符被分成i个子集时,
dp[i][j]
表示0和1的最小乘积之和。 -
初始化base cases−
dp[1][j]
为二进制字符串的前j个字符中0和1的总和。-
对于每个i,dp[i][i]为0(因为将长度为i的字符串分成一个子集时只有一个子集)。
-
对于每个i从2到K和每个j从i+1到n,使用递归关系找到
dp[i][j]: dp[i][j] = min(dp[i-1][k] + cost[k+1][j]
其中cost[k+1
][j]是子串从k+1到j的0和1的乘积。通过迭代i-2到j-2的所有可能的k值,可以确定dp[i][j]的最小值。 -
当二进制字符串被分成K个子集时,
dp[K][n]
提供0和1的最小乘积之和。
步骤
步骤1 − 创建一个(K+1)*(n+1)的二进制字符串的边数数组。
步骤2 − 将基本情况的初始化设置为dp[0][0] = 0,dp[i][0] = 正无穷(对于i>0),dp[0][j] = 正无穷(对于j>0)
。
第3步 - 对于每个 i 从 1 到 n,每个 j 从 1 到 K−,计算 dp[i][j]
。
- 将所有这些成本的最小值作为
dp[i][j]
。 -
对于每个 k 从 0 到 i-1,计算将二进制字符串从 k 到 i-1 分割成 j-1 个子集的成本,并将该子字符串中 0 和 1 的数量的乘积相加。
第4步 - 使用 dp[n][K]
作为将二进制字符串分割成 K 个子集的成本最低方式。
示例1
作为第一步,将所有二进制字符串的可能子串相加,将结果存储在 sum 数组中。然后,在将二进制字符串分割成 k 个子集的情况下,将最小乘积和存储在 dp 数组中,直到第 i 个字符。
#include <iostream>
#include <cstring>
#include <climits>
using namespace std;
const int MAXN = 105;
const int INF = INT_MAX;
int dp[MAXN][MAXN], sum[MAXN][MAXN];
int main() {
string s = "1001011101";
int K = 3;
int n = s.length();
memset(dp, 0, sizeof(dp));
memset(sum, 0, sizeof(sum));
for (int i = 1; i <= n; i++) {
sum[i][i] = s[i - 1] - '0';
for (int j = i + 1; j <= n; j++) {
sum[i][j] = sum[i][j - 1] + s[j - 1] - '0';
}
}
for (int k = 1; k <= K; k++) {
for (int i = 1; i <= n; i++) {
if (k == 1) {
dp[i][k] = sum[1][i] * (sum[1][n] - sum[i][n]);
} else {
dp[i][k] = INF;
for (int j = 1; j < i; j++) {
dp[i][k] = min(dp[i][k], dp[j][k - 1] + sum[j + 1][i] * (sum[1][n] - sum[i][n]));
}
}
}
}
cout << "Minimum sum of products: " << dp[n][K] << endl;
return 0;
}
输出
Minimum sum of products: -2147483624
方法2:贪婪方法
我们可以首先将二进制字符串分割成K个相等的部分。然后可以确定每个部分的成本,并通过交换相邻的部分来努力降低总成本。直到没有更多可行的交换或没有更多的成本节省为止,我们可以切换相邻的部分。
语法
- 给定二进制字符串s和整数K
-
二进制字符串s的长度
int n = s.length();
- cut[i] = s的前i位中1的个数
vector<int> cut(a+1);
for (int D = 0; D < a; D++) {
cut[D+1] = cut[D] + (s[D] == '1');
}
vector<int> ft(a+1, 1e9);
ft[0] = 0;
for (int R = 1; k <= R; R++) {
for (int D = a; D >= R; D--) {
for (int j = R-1; j < R; j++) {
dp[D] = min(ft[D], ft[j] + (cut[D] - cut[j]) * (cut[j] - cut[R-1]));
}
}
}
- 将s分割为K个子集合中,0和1的积的最小和
int ans = ft[a];
贪婪法算法
步骤1 - 确定二进制字符串的0和1的数量。
步骤2 - 从1的位数中减去0的位数。
步骤3 - 将二进制字符串分成K个相等的段。
步骤4 - 计算每个段中0和1的数量。
步骤5 - 为每个段减去0的位数从1的位数中。
步骤6 - 总乘积通过添加所有段的乘积获得。
步骤7 - 如果总乘积小于计算步骤2中确定的乘积,则将段作为解决方案返回。如果不是,则继续到步骤8。
步骤8 - 继续合并相邻段,直到获得K个段,这些段的0和1的乘积和最小。
示例2
在此之后,通过强调每个字符并将其添加到当前子集中,将该字符串分为K个子集,假设0和1的总重复次数不为负。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<string> splitBinaryString(string s, int k) {
vector<int> zeros(s.length()), ones(s.length());
int zeroCount = 0, oneCount = 0;
for (int i = s.length() - 1; i >= 0; i--) {
if (s[i] == '0') zeroCount++;
else oneCount++;
zeros[i] = zeroCount;
ones[i] = oneCount;
}
vector<string> subsets(k);
for (int i = 0; i < k; i++) {
subsets[i] = "";
int start = i, end = s.length() - k + i + 1;
for (int j = start; j < end; j++) {
int zeroOccurrences = zeros[j] - zeros[start];
int oneOccurrences = ones[j] - ones[start];
if (zeroOccurrences * oneOccurrences < 0) break;
subsets[i] += s[j];
}
}
return subsets;
}
int sumOfProducts(vector<string> subsets) {
int sum = 0;
for (string subset : subsets) {
int zeroCount = count(subset.begin(), subset.end(), '0');
int oneCount = subset.length() - zeroCount;
sum += zeroCount * oneCount;
}
return sum;
}
int main() {
string s = "1010110010";
int k = 3;
vector<string> subsets = splitBinaryString(s, k);
int result = sumOfProducts(subsets);
cout << "Minimum sum of products of occurrences of 0 and 1: " << result << endl;
return 0;
}
输出:
Minimum sum of products of occurrences of 0 and 1: 48
结论
当将二进制字符串分成K个子集时,可以使用动态规划来找到0和1的乘积的最小和。我们可以通过跟踪每个子集大小和每个起始位置的最小成本,快速确定最小成本的完整字符串。动态规划方法的时间复杂度为O(K * n2)
,其中n是二进制字符串的长度。