我如何修复我的C++代码来计算具有特定高度和节点数的唯一二叉树?

olhwl3o2  于 2023-05-23  发布在  其他
关注(0)|答案(1)|浏览(128)

我一直在研究USACO培训问题Cow Pedigrees(有关问题的解释和示例解决方案,请参见https://jvonk.github.io/usaco/2018/10/16/nocows.html
基本上,你必须计算节点数为N,高度为K的唯一二叉树的可能性的数量(mod 9901)。
下面是我目前为止尝试过的C++代码。它输出一些较小输入的正确答案,如N = 5; K = 3和N = 9;然而,一旦它到达N = 35和K = 7的测试用例,它就输出7642而不是5024的正确答案。
我一生都无法弄清楚为什么会这样,因为我的代码对我来说都是有意义的,当我用较小的数字完成这些步骤时,它似乎一切都正确,就像我手动完成一样。我认为这个问题与它有关,重复计算不应该被重复计算的可能性(在if else语句中),但我不知道为什么这是真的。
如果有人能解释一下我的方法哪里出错了,以及我如何纠正它,我将非常感激!先谢谢你了

#include <bits/stdc++.h>

using namespace std;

int main() {
  // values
  int N, K;
  int possibilities[201][101]{};

  // input
  ifstream fin("nocows.in");
  fin >> N >> K;

  // solution
  possibilities[1][1] = 1;
  for (int k = 2; k <= K; k++){ // for every possible height
    // 1 node is used for the "mother", the amount of the  rest of the nodes will be between 2*(k - 1) and -2 * (1-2^(k-1))
    for (int n = 2 * (k - 1); n <= 2 * (pow(2, k - 1) - 1) && n <= N; n += 2 ){
      // one side will have n1 nodes - anywhere odd number from 1 to n; and a height of k - 1
      for (int n1 = 1; n1 < n; n1 += 2){
        // the other side will have any height k - 1 or less and the remaining nodes
        for (int k1 = 1; k1 < k; k1++){
          // if they are different numbers, they will be doubled because they are not interchangable so can work for both right and left
          if (n1 != n - n1 || k1 != k - 1) 
            possibilities[n + 1][k] += (2 * possibilities[n1][k - 1] * possibilities[n - n1][k1]) % 9901;
          else
            possibilities[n + 1][k] += (possibilities[n1][k - 1] * possibilities[n - n1][k1]) % 9901;
          possibilities[n + 1][k] %= 9901;
        }
      }
    }
  }

  // output
  ofstream fout("nocows.out");
  fout << possibilities[N][K] % 9901 << '\n';

  return 0;
}
pengsaosao

pengsaosao1#

你可以简化你的思维方式,把原来的问题分解成两个更小的问题。
在这个树中,每个节点要么总是有两个孩子,要么没有孩子。因此,根(您称之为“母亲”)要么有两个孩子,要么没有孩子。有零个孩子的根是平凡的。它对应于N = 1和K = 1。
当根有两个孩子时,这些孩子中的每一个都可以是它们自己的树的根。仔细看看这些子树。这些树彼此完全独立。它们中的每一个都以与原始树相同的方式运行。只要它们中的一个具有k-1的深度,它们一起形成原始树的有效排列。
因此,您的代码应该通过以下方式简化:

  • n1不必是奇数。它可以是任何数字<= n-1。另一棵树将有n2 = n - 1 - n1个节点。记住,已经有一个根节点,即n1 + n2 = n-1
  • 有四个变量决定了所有的可能性:n1n2k1k2k1k2是两个子树的深度。
  • 在for循环中,您需要确保条件k1k2中至少有一个等于k-1。只有这样,您才能获得与原始树相对应的配置。
  • 您仍然需要小心(k1 == k-1) && (k2 == k-1)的情况,不要重复计算它。

修改给定代码的一种方法是:

#include <algorithm>
#include <cmath>
#include <fstream>
#include <iostream>

int main() {
  // values
  int N, K;
  int possibilities[201][101]{};

  // input
  std::ifstream fin("nocows.in");
  fin >> N >> K;

  // solution
  possibilities[1][1] = 1;
  for (int k = 2; k <= K; k++){ // for every possible height
    // 1 node is used for the root, the total nodes will be 
    // between 2*(k - 1) + 1 and min(2 * (2^(k-1) - 1) + 1, N)
    for (int n = 2 * (k - 1) + 1;
         n <= std::min(static_cast<int>(2 * (pow(2, k - 1) - 1)) + 1, N);
         n += 2 ){
      // The root has two children. Each of the two children could be treated as 
      // roots of their own trees. Thus, the total number of possibilities for 
      // the original is equal to the sum of the product of pairs of these cases.
      // one side will have n1 nodes where n1 <= n - 1 
      // the other side will have n2 = n - 1 -n1
      // The two trees are independent, with the constraint being at least one 
      // should have a depth of k-1.
      for (int n1 = 1; n1 <= n-1; ++n1){
        int n2 = n - 1 - n1;
        for (int kv = 1; kv < k-1; kv++){
          // constrain either of the depth to k-1 thus, count both the given
          // configuration and the switched configuration.

          possibilities[n][k] +=
            2 * possibilities[n1][kv] * possibilities[n2][k-1];
        }
        // deal with the case of both depths being k-1 separately
        possibilities[n][k] += possibilities[n1][k-1] * possibilities[n2][k-1];
      }
    }
  }

  /*
  for (int n = 0; n <= N; ++n) {
    for (int k = 0; k <= K; ++k) {
      std::cout<<" "<<possibilities[n][k];
    }
    std::cout<<"\n";
  }
  */

  // output
  std::ofstream fout("nocows.out");
  fout << possibilities[N][K] % 9901 << '\n';

  return 0;
}

相关问题