c – 具有k 1位的最小n位整数c,是g,h位设置为1的两个n位整数之和(动态编程)

我试图解决以下问题:

Find the smallest n-bit integer c that has k 1-bits and is the sum of two n-bit integers that have g,h bits set to 1. g,h,k <= n

首先,这里的n位整数意味着我们可以使用所有n位,即最大值.这样的整数的值是2 ^ n-1.所描述的整数可能根本不存在.
很明显,情况k> g h没有解,对于g h = k,答案​​只是2 ^ k – 1(前k位是1位,前面是k-n个零).

至于程序应该做什么的一些例子:

g = h = k = 4,n = 10 :
0000001111 + 0000001111 = 0000011110
15 + 15 = 30 (30 should be the output)


(4,6,5,10):
0000011110 + 0000111111 = 0001011101
30 + 63 = 93

(30,1,31):
1 + (2^30 - 1) = 2^30

我想到这一点,这是一个动态编程问题,我选择了以下方法
令dp [g] [h] [k] [n] [c]为所述整数,c为携带的可选位.我尝试根据最低位重建可能的总和.
所以,dp [g] [h] [k] [n 1] [0]是最小的

(0,0):       dp[g][h][k][n][0]
(0,0): 2^n + dp[g][h][k - 1][n][1]
(1,0): 2^n + dp[g - 1][h][k - 1][n][0]
(0,1): 2^n + dp[g][h - 1][k - 1][n][0]

类似地,dp [g] [h] [k] [n 1] [1]是最小值

(1,1): dp[g - 1][h - 1][k][n][0]
(1,1): dp[g - 1][h - 1][k - 1][n][1] + 2^n
(1,0): dp[g - 1][h][k][n][1]
(0,1): dp[g][h - 1][k][n][1]

这个想法并不那么难,但我对这些事情并不是很有经验,即使对于最简单的情况,我的算法也不起作用.我选择了自上而下的方法.我很难考虑所有的角落案例.我真的不知道我是否正确选择了递归的基础等.我的算法甚至不适用于g = h = k = 1,n = 2的最基本情况(答案是01 01 = 10) .对于g = h = k = 1,n = 1,应该没有答案,但算法给出1(这基本上是为什么前一个例子输出1而不是2).
所以,这里是我可怕的代码(只有非常基本的C):

int solve(int g,int h,int k,int n,int c = 0) {
  if (n <= 0) {
    return 0;
  }
  if (dp[g][h][k][n][c]) {
    return dp[g][h][k][n][c];
  }
  if (!c) {
    if (g + h == k) {
      return dp[g][h][k][n][c] = (1 << k) - 1;
    }
    int min,a1,a2,a3,a4;
    min = a1 = a2 = a3 = a4 = std::numeric_limits<int>::max();
    if (g + h > k && k <= n - 1) {
      a1 = solve(g,k,n - 1,0);
    }
    if (g + h >= k - 1 && k - 1 <= n - 1) {
      a2 = (1 << (n - 1)) + solve(g,k - 1,1);
    }
    if (g - 1 + h >= k - 1 && k - 1 <= n - 1) {
      a3 = (1 << (n - 1)) + solve(g - 1,0);
    }
    if (g + h - 1 >= k - 1 && k - 1 <= n - 1) {
      a4 = (1 << (n - 1)) + solve(g,h - 1,0);
    }
    min = std::min({a1,a4});
    return dp[g][h][k][n][c] = min;
  } else {
    int min,a4;
    min = a1 = a2 = a3 = a4 = std::numeric_limits<int>::max();
    if (g - 2 + h >= k && k <= n - 1) {
      a1 = solve(g - 1,0);
    }
    if (g - 2 + h >= k - 1 && k - 1 <= n - 1) {
      a2 = (1 << (n - 1)) + solve(g - 1,1);
    }
    if (g - 1 + h >= k && k <= n - 1) {
      a3 = solve(g - 1,1);
    }
    if (g - 1 + h >= k && k <= n - 1) {
      a4 = solve(g,1);
    }
    min = std::min({a1,a4});
    return dp[g][h][k][n][c] = min;
  }
}

解决方法

您可以根据位计数g,h和k构建最小和,而无需进行任何动态编程.假设g≥h(否则切换它们)这些是规则:

k≤h≤g

11111111  <-  g ones  
  111100000111  <-  h-k ones + g-k zeros + k ones
 1000000000110  <-  n must be at least h+g-k+1

h≤k≤g

1111111111  <-  g ones  
      11111100  <-  h ones + k-h zeros
    1011111011  <-  n must be at least g+1

h≤g≤k

1111111100000  <-  g ones + k-g ones  
 1100000011111  <-  g+h-k ones,k-h zeros,k-g ones
11011111111111  <-  n must be at least k+1,or k if g+h=k

示例:对于n = 10,g = 6和h = 4,k的所有值均为:

k=1           k=2           k=3           k=4       
0000111111    0000111111    0000111111    0000111111
0111000001    0011000011    0001000111    0000001111
----------    ----------    ----------    ----------
1000000000    0100000010    0010000110    0001001110
k=4           k=5           k=6
0000111111    0000111111    0000111111
0000001111    0000011110    0000111100
----------    ----------    ----------
0001001110    0001011101    0001111011
k=6           k=7           k=8           k=9           k=10
0000111111    0001111110    0011111100    0111111000    1111110000
0000111100    0001110001    0011000011    0100000111    0000001111
----------    ----------    ----------    ----------    ----------
0001111011    0011101111    0110111111    1011111111    1111111111

或者,直接转到c的值而不先计算a和b:

k≤h≤g

c = (1 << (g + h - k)) + ((1 << k) - 2)

h≤k≤g

c = (1 << g) + ((1 << k) - 1) - (1 << (k - h))

h≤g≤k

c = ((1 << (k + 1)) - 1) - (1 << ((g - h) + 2 * (k - g)))

h g = k

c = (1 << k) - 1

这导致了这个令人失望的平凡代码

int smallest_sum(unsigned n,unsigned g,unsigned h,unsigned k) {
    if (g < h) {unsigned swap = g; g = h; h = swap;}
    if (k == 0) return (g > 0 || h > 0 || n < 1) ? -1 : 0;
    if (h == 0) return (g != k || n < k) ? -1 : (1 << k) - 1;
    if (k <= h) return (n <= h + g - k) ? -1 : (1 << (g + h - k)) + ((1 << k) - 2);
    if (k <= g) return (n <= g) ? -1 : (1 << g) + ((1 << k) - 1) - (1 << (k - h));
    if (k < g + h) return (n <= k) ? -1 : (1 << (k + 1)) - 1 - (1 << (2 * k - g - h));
    if (k == g + h) return (n < k) ? -1 : (1 << k) - 1;
    return -1;
}

一些示例结果:

n=31,g=15,h=25,k=10  ->  1,073,742,846  (1000000000000000000001111111110)
n=31,k=20  ->     34,602,975  (0000010000011111111111111011111)
n=31,k=30  ->  2,146,435,071  (1111111111011111111111111111111)

(我将结果与蛮力算法的结果进行比较,对于n,g,h和k的每个值,从0到20来检查正确性,并且没有发现差异.)

相关文章

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评...
水了一学期的院选修,万万没想到期末考试还有比较硬核的编程...
补充一下,先前文章末尾给出的下载链接的完整代码含有部分C&...
思路如标题所说采用模N取余法,难点是这个除法过程如何实现。...
本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码...
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊...