宵暗的妖怪
错误代码和正确代码对比
#include <iostream>
#include <string.h>
using namespace std;
const int N=1e5+10;
long long a[N],f[N],g[N];
// f 以i为结尾,饱食度最大值
// g 0-i中,饱食度最大值
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
// 错误代码
long long ret = 0;
for(int i=1;i<=n;i++){
if(i-3>=0) f[i]=max(f[i],f[i-3]+a[i-1]);
ret = max(f[i],ret);
}
cout << ret << endl;
for(int i=1;i<=n;i++) cout << f[i] << " ";
cout << endl;
// 正确代码
for(int i=3;i<=n;i++){
g[i]=max(g[i-1],g[i-3]+a[i-1]);
}
// cout << g[n] << endl;
for(int i=1;i<=n;i++) cout << g[i] << " ";
cout << endl;
return 0;
}
逐步分析错误
转移方程表示
f表示以i位置为结尾(选择i位置),饱食度最大值
g表示0到i中,饱食度最大值
错误想法
根据f表示的意义,使用ret记录每个位置f的最大值,最后的ret记录的不就是所有值中的最大值吗
解决错误
当通过了全部的测试样例,并且自己认为是对的的时候已经很难发现错误的点了,这时候就需要通过使f转移方程错误的测试用例来反思
样例生成程序
#include <iostream>
#include <string.h>
using namespace std;
const int N = 1e5 + 10;
long long a[N], f[N], g[N];
// f 以i为结尾,饱食度最大值
// g 0-i中,饱食度最大值
int Create_num()
{
return rand() % N;
}
void Print(int n)
{
cout << " n : " << n << endl;
for (int i = 0; i <= n; i++) cout << a[i] << " ";
puts("");
for (int i = 0; i <= n; i++) cout << f[i] << " ";
puts("");
for (int i = 0; i <= n; i++) cout << g[i] << " ";
cout << endl;
}
void test1()
{
while (1) {
memset(f, 0, sizeof(f));
memset(g, 0, sizeof(g));
memset(a, 0, sizeof(a));
int n = Create_num();
// 11207 14070 22741 7395 14310 17603 2604 9444
for (int i = 1; i <= n; i++) a[i] = Create_num();
long long ret = 0;
for (int i = 1; i <= n; i++) {
if (i - 3 >= 0) f[i] = max(f[i], f[i - 3] + a[i - 1]);
ret = max(f[i], ret);
}
for (int i = 3; i <= n; i++) {
g[i] = max(g[i - 1], g[i - 3] + a[i - 1]);
}
if (g[n] != ret) {
if (n < 10) {
cout << "ret: " << ret << " g[n]: " << g[n] << endl;
Print(n);
break;
}
cout << n << endl;
}
}
}
void test2()
{
int n = 8;
//cout << n << endl;
// 0 32499 8997 17774 7402 23203 5981 30764 26296 9816
int nums[100] = { 0, 18010, 12435 ,1226 ,12340, 21433 ,24078, 28114 ,18926 };
for (int i = 1; i <= n; i++) a[i] = nums[i];
long long ret = 0;
for (int i = 1; i <= n; i++) {
if (i - 3 >= 0) {
f[i] = max(f[i], f[i - 3] + a[i - 1]);
g[i] = max(g[i - 1], g[i - 3] + a[i - 1]);
}
ret = max(f[i], ret);
}
cout << ret << endl;
for (int i = 1; i <= n; i++) cout << f[i] << " ";
cout << endl;
//for (int i = 3; i <= n; i++) {
// g[i] = max(g[i - 1], g[i - 3] + a[i - 1]);
//}
cout << g[n] << endl;
for (int i = 1; i <= n; i++) cout << g[i] << " ";
cout << endl;
/*
0, 18010, 12435 ,1226 ,12340, 21433 ,24078, 28114 ,18926
0454
0 0 12435 1226 12340 33868 25304 40454
40549
0 0 12435 12435 12435 33868 36513 40549
*/
}
int main()
{
puts("00");
srand(time(NULL));
//test1();
test2();
return 0;
}
错误样例分析
0, 18010, 12435 ,1226 ,12340, 21433 ,24078, 28114 ,18926
最大值应该产生在 18010, 12435 ,1226 和 21433 ,24078, 28114
,而不是简简单单的向前跳3步;知道了错误点再去理解正确的代码也就没有什么顾虑了。
总结
遇到自认为对的代码,应该和正确代码同时测试相同的用例,并对比他们最后不同的点,并找出原因。