【问题标题】:Generate subset of series {1,3,9,27,....} and order the subsets in increasing order of sum生成序列 {1,3,9,27,....} 的子集并按总和的递增顺序对子集进行排序
【发布时间】:2015-02-16 09:35:41
【问题描述】:

我被要求生成 3 次幂集 {1,3,9,27,81,....} 的子集,然后按总和的升序排列子集,例如 { {},{ 1},,{3},{1,3},{9},{1,9},{3,9},{1,3,9},..........}。最后,当用户输入 n 时,我们需要打印第 n 个子集,例如如果 n=4,则解决方案是第三个子集 {1,3}。 n 的范围非常大。

我的疑问是:

  1. 我们如何达到主系列的上限。
  2. 我应该使用哪种数据结构
  3. 除了蛮力还有其他方法吗。

【问题讨论】:

  • 添加类别“算法”
  • 您的问题看起来像“以 3 为基数计数”。因此,我认为有比蛮力更好的解决方案。
  • 我是初学者,如果有人可以指导我应该如何进行
  • 在您的示例输出中,您缺少集合 {3}{9}

标签: java algorithm numbers set


【解决方案1】:

对于这个问题,3 的幂或 2 的幂是相同的,但是用 2 的幂更容易看到解决方案。看顺序:

  • {} 总和:0
  • {1} 总和:1
  • {2} sum : 2(二进制为 10)
  • {1, 2} sum : 3(二进制为 11)
  • {4} sum : 4(二进制为 100)
  • {4, 1} sum : 5(二进制为 101)
  • {4, 2} sum : 6(二进制为 110)

因此,解决方案是以二进制计数,然后以 3 为基数解释给定的数字。例如:

  • 0 -> {}
  • 1 -> 3^0 -> {1}
  • 10 -> 3^1 -> {3}
  • 11 -> 3^1, 3^0 -> {3, 1}
  • 100 -> 3^2 -> {9}
  • 101 -> 3^2, 3^0 -> {9, 1}
  • 110 -> 3^2, 3^1 -> {9,3}
  • 111 -> 3^2, 3^1, 3^0 -> {9,3,1}
  • 1000 -> 3^3 -> {27}

因此,对于给定的n,将其转换为二进制,然后再转换为上述的三进制。

这是代码

private static String calc(int n) {
  String binary = Integer.toString(n - 1, 2);
  String reverse = new StringBuilder(binary).reverse().toString();

  StringBuilder sb = new StringBuilder();
  sb.append("{");
  for (int i = 0; i < reverse.length(); i++) {
    if(reverse.charAt(i) == '1') {
      sb.append(BigInteger.valueOf(3).pow(i)).append(" ");
    }
  }
  //remove last space
  if(sb.length() != 1) { sb.deleteCharAt(sb.length() -1); }
  sb.append("}");
  return sb.toString();
}

测试

for(int i = 1; i<=10; i++) {
  System.out.println(calc(i));
}

结果

{}
{1}
{3}
{1 3}
{9}
{1 9}
{3 9}
{1 3 9}
{27}
{1 27}

【讨论】:

  • 这对我来说似乎是正确的。唯一的问题是,根据 OP 的约定,您需要从 n 中减去 1,然后应用您的二进制转换。
【解决方案2】:
  1. 您不会先创建大量子集,然后再对其进行排序。您以已排序的顺序创建子集(这是算法的关键部分)。当您创建了“n”个子集后,您将返回。
  2. 您需要自己制作。如果我没记错的话,我在做类似的事情时只是使用了队列。
  3. 我不知道。问题是您需要订购子集,这并非易事。

【讨论】:

    【解决方案3】:

    除了蛮力还有什么办法。

    是的,有。尝试生成有序的集合序列。我们可以证明这将使用归纳法起作用。

    1. 总和最小的集合是{}

    2. 假设您已经订购了所有包含 N 以下数字的子集。

    3. 要生成包含 N 的所有子集,只需将 N 添加到步骤 2 中的每个集合(以相同的顺序)

    这将起作用,因为以下是正确的 - 3^n > 3^n-1 + 3^n-2 .... 例如子集总和({3^n})大于所有子集的总和仅包含 3^n-1 和更小的元素。

    示例

    1. {1} 是最小子集
    2. {}、{1}、{3}、{1,3}、{9}、{1,9}、{3,9}、{1,3,9} - 包含数字小于的有序子集大于 9
    3. {}、{1}、{3}、{1,3}、{9}、{1,9}、{3,9}、{1,3,9}、{27}、{1 ,27}, {3,27}, {9,27} .... {1,3,9,27} - 包含小于 27 的数字的有序子集

    【讨论】:

      【解决方案4】:

      我们只需将给定的 'n' 转换为二进制,然后提高 3^i 的幂(对于第 i 位 仅当它是setbit),因为只有 setbits 会对子集有贡献。

      由于以下条件,这将起作用:(3^n > 3^n-1 + 3^n-2) 例如:3^2 > 3^1 + 3^0

      现在是代码:

      void decimalToBinary(long int n) 
      { 
      int binaryNum[32];                        // array to store binary number  
      int i = 0;                                // counter for binary array
      while (n > 0) {                           // storing remainder in binary array
          binaryNum[i] = n % 2; 
          n = n / 2; 
          i++; 
      } 
      for (int j = 0; j <i; j++) 
      {
          if(binarray[j]==1)
              cout<<binpower(3,j)<<" ";              //must be binary exponentiation
      }
      } 
      

      输入:6

      输出:3 9

      【讨论】:

      • 别忘了使用二进制求幂
      【解决方案5】:

      很容易看出,如果在 n 的二进制表示中设置了第 i 位,则可以通过打印集合 S 的第 i 个元素来生成子集,如果未设置该位,则不打印任何内容。

      1. 初始化 count=0 和 x=0
      2. 用于存储子集元素的向量
      3. 执行以下操作直到 n 退出
        • set x=n&1 // 用于判断最后一位是否已设置
        • 现在如果 x 不为零,请执行此操作
          • 通过 pow(3,count) 将元素推入向量中
        • 然后在 n (n=n>>1) 上右移一次
        • 计数值加1
      4. 最后打印vector的元素

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-02-15
        • 1970-01-01
        • 2011-03-30
        • 2015-05-27
        • 1970-01-01
        • 1970-01-01
        • 2015-03-29
        • 2011-04-06
        相关资源
        最近更新 更多