将二进制字符串分割成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]来确定dp[i][j]的值,其中cost[k+1][i]是将从k+1到i的子串分割的代价。代价由子串中0和1的数量的乘积确定。
语法
下面提供动态规划方法的语法:
创建一个名为dp的二维表,其维度为K+1乘以n+1,其中K是子集的数量,n是二进制字符串的长度。当二进制字符串的前j个字符被分成i个子集时,dp[i][j]表示0和1的乘积之和的最小值。
初始化边界条件:
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的乘积。可以通过迭代所有可能的k值(从i-2到j-2)来找到dp[i][j]的最小值。
当二进制字符串被分成K个子集时,dp[K][n]给出0和1乘积和的最小值。
算法
步骤1 - 创建一个 (K+1)*(n+1) 维的数组来存储二进制字符串的分割结果。
步骤2 - 将边界条件初始化为 dp[0][0] = 0, i > 0 时 dp[i][0] = 无穷大, j > 0 时 dp[0][j] = 无穷大。
步骤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个子集到第i个字符后,将最小乘积和存储在dp数组中。
#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 - 对于每个部分,用 1 的数量减去 0 的数量。
步骤6 - 将所有部分的乘积相加得到总乘积。
步骤7 - 如果总乘积小于步骤2中计算的结果,则返回这些部分作为解决方案。否则,转到步骤8。
步骤8 - 通过合并具有最小 0 和 1 乘积和的相邻部分,直到得到 K 个部分。
示例2
然后,通过遍历每个字符并将它们添加到当前子集(假设 0 和 1 的总冗余数不为负),将字符串划分为 K 个子集。
#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 * n^2),其中n是二进制字符串的长度。
数据结构
网络
关系数据库管理系统 (RDBMS)
操作系统
Java
iOS
HTML
CSS
Android
Python
C语言编程
C++
C#
MongoDB
MySQL
Javascript
PHP