C++ 将二进制字符串拆分为K个子集,使得0和1的出现次数乘积之和最小

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是二进制字符串的长度。

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程