C++ 如何验证竞争性编程任务的动态规划解决方案的正确性?

C++ 如何验证竞争性编程任务的动态规划解决方案的正确性?,c++,recursion,dynamic-programming,C++,Recursion,Dynamic Programming,我正试图开发出一种系统的方法来提出动态规划的DP解决方案——遵循某些步骤,您可以提出一个有效的问题解决方案 其基本思想是:从递归开始,对其进行调优以最小化参数数量,这允许您定义问题并添加记忆,然后您可以轻松地提出DP解决方案。事实证明,事情并没有那么简单 不能简单地将递归解转换为DP。 例如,参见USACO提供的子集和PS 下面是使用我的方法将递归转换为DP的代码 #include <cstdio> #include <iostream> #include <cst

我正试图开发出一种系统的方法来提出动态规划的DP解决方案——遵循某些步骤,您可以提出一个有效的问题解决方案

其基本思想是:从递归开始,对其进行调优以最小化参数数量,这允许您定义问题并添加记忆,然后您可以轻松地提出DP解决方案。事实证明,事情并没有那么简单

不能简单地将递归解转换为DP。 例如,参见USACO提供的子集和PS

下面是使用我的方法将递归转换为DP的代码

#include <cstdio>
#include <iostream>
#include <cstdint>
#include <cstring>
using namespace std;
const int MAX_SUBSETSUM = (39+1)*39/4;
const int MAX_N = 39;
int memo[MAX_SUBSETSUM+1][MAX_N+1];
const int NOT_VISITED = -1;
// resembles 0-1 knapsack problem
// we need to select numbers that will go to
// the first set and those sum is totalSum/2
// so we have two sets of equal sum
int recur(int sum, int num) {
  int counter = 0;
  // ### 1.Base case
  if(sum == 0)
    return 1;
  if(sum < 0 || num <= 0)
    return 0;

  // ### Memoization
  if(memo[sum][num] != NOT_VISITED)
    return memo[sum][num];

  // ### 2.Recursive step
  for(int nextNum = 1; nextNum < num; ++nextNum) {
    if(nextNum > sum)
      break;
    //### 3. Make a decision
    const int withNumber = recur(sum - num, nextNum);
    const int withOutNumber = recur(sum, nextNum);
    counter = withNumber + withOutNumber;
  }
  memo[sum][num] = counter;
  return counter;
}


int solveDP(int subsetSum, int N) {
  if(subsetSum & 1)
    return 0;

  int dp[MAX_SUBSETSUM+1][MAX_N+1];
  memset(dp,0,sizeof(dp));
  // fill in base cases
  for(int i = 0; i <= N; ++i) {
    // when subsetsum is 0, we have one way (we pick no nubers)
    dp[0][i] = 1;
  }

  for(int sum =1; sum <=subsetSum; ++sum) {
    for(int num = 1; num <= N; ++num) {
      if(sum >= num)
        dp[sum][num] = dp[sum - num][num-1]/*with num*/ + dp[sum][num-1]/*without*/;
      else 
        dp[sum][num] = dp[sum][num-1]/*without*/;
    }
  }
  return dp[subsetSum][N]/2; // NB!!! /2 - came up with it
}

int main() {
  //freopen("preface.in", "r", stdin);
  //freopen("preface.out", "w", stdout);
  const int N = 7;
  const int subsetSum = (N+1)*N/4;
  memset(memo, NOT_VISITED, sizeof(memo));
  int res = recur(subsetSum, N);
  res = solveDP(subsetSum, N);
  return 0;
}
在DP中,您必须将结果减半。请参见返回DP[subsetSum][N]/2;。我发现它是因为我得到了两个版本,结果不同,所以它涉及到一些类似于试错法的东西来调整DP到递归。 在我用纸和笔玩了DP表之后,我才明白为什么会这样,所以我注意到结果是重复计算的。 这对我很有帮助

但当你在编程竞赛中遇到DP问题时,你该怎么办?你的时间有限,显然,你负担不起玩DP表, 您能提供一些技术建议来验证我的DP解决方案的结果,以确保它是正确的


我的目标是减少错误尝试的时间和次数。

制作一个简单的暴力程序,并运行一个小的测试输入来比较
暴力和你的DP计划的结果。当有疑问时,使用暴力验证最简单的方法是交叉检查结果:使用两种方法生成结果,并确保它们相等。如果测试失败,你就有问题了。@Ober,我就是这么做的。这里可能存在瓶颈-你可以有两个错误的版本收敛到相同的结果,在我看来,很明显,要编写两个版本,你需要在一个版本上花费更多的时间,考虑到这一切都发生在竞争期间,你的提交时间是定时的,我不认为这是最有效的方法,虽然有效