我正在尝试开发系统方法来提出动态编程(DP)解决方案 - 按照某些步骤,您可以找到有效的问题解决方案。
理念,基本上是以下内容:从递归开始,调整它以最小化参数数量,这允许您定义问题并添加记忆,之后您可以轻松想出一个DP解决方案。事实证明,事情并非那么简单。
不能简单地将递归解决方案转移到DP中。 例如。查看来自USACO http://train.usaco.org/usacoprob2?a=9OS8tkGfsX5&S=subset
的子集和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中你必须得到一半的结果(见return dp[subsetSum][N]/2;
)。我发现它是因为我有两个版本,结果不同,所以它涉及像试错法调整DP到递归。
我能够理解为什么只有在用纸和笔在DP桌上玩之后才会发生什么,所以我注意到结果是双重计算的。
这帮助了我。
但是当你在编程比赛中遇到DP问题时该怎么办,你的时间有限,显然不能用DP桌来玩, 你可以建议一些技术,以验证我的DP解决方案的结果,以确保它是正确的。
我的目标是减少错误尝试的时间和次数。
答案 0 :(得分:-2)
制作一个简单的蛮力程序并运行一个小的测试输入以比较 暴力和您的DP程序导致的结果。 “如有疑问,请使用蛮力”