如果将 Memoization 添加到 Recursion,则错误的解决方案
Wrong Solution if Memoization is added to Recursion
我已经创建了一个 DP 程序,但问题是当我不使用记忆时我得到了正确的答案。一引入记忆化,我就开始对某些问题得到错误的答案
这是 C++ 14 中关闭记忆的代码(通过注释)
#include <iostream>
#include <math.h>
#include<algorithm>
using namespace std;
int max_Number_of_turns;
int dp[9999][1000];
int changeTheDigit(int n, int d) {
int rem = n % (int) (pow(10, 4 - d));
n /= (pow(10, 4 - d));
int x = n % 10;
n /= 10;
if (x == 9) x = 0;
else x = x + 1;
n = n * (10) + x;
n = n * (pow(10, 4 - d)) + rem;
return n;
}
int minMax(int n, int t) {
int ans =0;
//if(dp[n][t]>=0) { return dp[n][t];}
if (t > max_Number_of_turns) return n;
int N;
for (int i = 0; i < 4; i++) {
N = changeTheDigit(n, i + 1);
if (t % 2 == 0) {
//Manish chance
if(ans==0) ans=minMax(N, t+1);
else ans = min(ans, minMax(N, t + 1));
} else {
//Nitish Chance
ans = max(ans, minMax(N, t + 1));
}
}
//cout << ans << endl;
dp[n][t]=ans;
return ans;
}
using namespace std;
int main() {
int T, N, M;
cin >> T;
while (T--) {
cin >> N >> M;
max_Number_of_turns=M;
for(int i=0;i<9999;i++)
for(int j=0;j<1000;j++)
dp[i][j]=-1;
if(minMax(N,1)>N){
cout << "Nitish" << endl;
}
else{
cout << "Manish" << endl;
}
}
return 0;
}
打开记忆评论(即删除该行的评论)
if(dp[n][t]>=0) { return dp[n][t];}
而且我的代码会给出一些错误的答案
例如,让我们考虑输入
1
4569 12
原正确解是Manish
但是如果我打开记忆,我的解决方案是 Nitish
你能告诉我我做错了什么吗
此外,一个有趣的事实是,如果将 DP 代码从
if(dp[n][t]>=0) { return dp[n][t];}
到
if(dp[n][t]>0) { return dp[n][t];}
那就一切都好
您的问题是 n
and/or t
的值未被检查,因此可能导致数组出现越界问题。您可以看到,如果您在 minMax
函数的开头插入以下内容:
if (n < 0 || n >= 9999) cout << "n invalid at " << n << '\n';
if (t < 0 || t >= 1000) cout << "t invalid at " << t << '\n';
运行 您的示例输入在输出结果之前发出警告:
n invalid at 9999
n invalid at 9999
n invalid at 9999
要解决此问题,您可以确保仅在有足够存储空间时才使用记忆,首先是 检查 值:
if (n >= 0 && n < 9999 && t >= 0 && t < 1000 && dp[n][t] >= 0)
return dp[n][t];
其次,当存储值时:
if (n >= 0 && n < 9999 && t >= 0 && t < 1000)
dp[n][t] = ans;
我已经创建了一个 DP 程序,但问题是当我不使用记忆时我得到了正确的答案。一引入记忆化,我就开始对某些问题得到错误的答案
这是 C++ 14 中关闭记忆的代码(通过注释)
#include <iostream>
#include <math.h>
#include<algorithm>
using namespace std;
int max_Number_of_turns;
int dp[9999][1000];
int changeTheDigit(int n, int d) {
int rem = n % (int) (pow(10, 4 - d));
n /= (pow(10, 4 - d));
int x = n % 10;
n /= 10;
if (x == 9) x = 0;
else x = x + 1;
n = n * (10) + x;
n = n * (pow(10, 4 - d)) + rem;
return n;
}
int minMax(int n, int t) {
int ans =0;
//if(dp[n][t]>=0) { return dp[n][t];}
if (t > max_Number_of_turns) return n;
int N;
for (int i = 0; i < 4; i++) {
N = changeTheDigit(n, i + 1);
if (t % 2 == 0) {
//Manish chance
if(ans==0) ans=minMax(N, t+1);
else ans = min(ans, minMax(N, t + 1));
} else {
//Nitish Chance
ans = max(ans, minMax(N, t + 1));
}
}
//cout << ans << endl;
dp[n][t]=ans;
return ans;
}
using namespace std;
int main() {
int T, N, M;
cin >> T;
while (T--) {
cin >> N >> M;
max_Number_of_turns=M;
for(int i=0;i<9999;i++)
for(int j=0;j<1000;j++)
dp[i][j]=-1;
if(minMax(N,1)>N){
cout << "Nitish" << endl;
}
else{
cout << "Manish" << endl;
}
}
return 0;
}
打开记忆评论(即删除该行的评论)
if(dp[n][t]>=0) { return dp[n][t];}
而且我的代码会给出一些错误的答案 例如,让我们考虑输入
1
4569 12
原正确解是Manish 但是如果我打开记忆,我的解决方案是 Nitish
你能告诉我我做错了什么吗
此外,一个有趣的事实是,如果将 DP 代码从
if(dp[n][t]>=0) { return dp[n][t];}
到
if(dp[n][t]>0) { return dp[n][t];}
那就一切都好
您的问题是 n
and/or t
的值未被检查,因此可能导致数组出现越界问题。您可以看到,如果您在 minMax
函数的开头插入以下内容:
if (n < 0 || n >= 9999) cout << "n invalid at " << n << '\n';
if (t < 0 || t >= 1000) cout << "t invalid at " << t << '\n';
运行 您的示例输入在输出结果之前发出警告:
n invalid at 9999
n invalid at 9999
n invalid at 9999
要解决此问题,您可以确保仅在有足够存储空间时才使用记忆,首先是 检查 值:
if (n >= 0 && n < 9999 && t >= 0 && t < 1000 && dp[n][t] >= 0)
return dp[n][t];
其次,当存储值时:
if (n >= 0 && n < 9999 && t >= 0 && t < 1000)
dp[n][t] = ans;