- 问答
P2615 [NOIP 2015 提高组] 神奇的幻方为什么用int创建数组会过不了
- @ 2026-1-21 20:13:25
#include <bits/stdc++.h>
using namespace std;
int n, k = 1, a, b;
int main ()
{
cin >> n;
// int c[41][41];
vector<vector<int>> c(n+2, vector<int>(n+2, 0));
c[1][n/2+1] = 1;
a = 1; b = n/2+1;
for ( int i = 2; i <= n * n; i ++)//a行 b列
{
if ( a != 1 && b != n)
{
if ( c[a-1][b+1] == 0)
{
c[a-1][b+1] = i;
a = a-1; b = b+1;
}
else
{
c[a+1][b] = i;
a = a+1; b = b;
}
}
else if ( a == 1 && b != n)
{
c[n][b+1] = i;
a = n; b = b+1;
}
else if ( a != 1 && b == n)
{
c[a-1][1] = i;
a = a-1; b = 1;
}
else if ( a == 1 && b == n)
{
c[a+1][b] = i;
a = a+1;b = b;
}
//if (c[n][n/2+1] == n * n) break;
}
for ( int i = 1; i <= n; i ++)
{
for ( int j = 1; j <= n; j ++)
{
if ( j != 1) cout << ' ';
cout << c[i][j];
}
cout << '\n';
}
return 0;
}
1 comments
-
Myobot @ 2026-1-21 20:14:06
同学你好,我看到你上传的代码,并注意到你的问题:“为什么用int创建数组会过不了”。首先,我注意到你的代码中已经将原本注释掉的
int c[41][41]替换为了vector<vector<int>> c(n+2, vector<int>(n+2, 0))。这是一个很好的观察起点。让我先分析一下你的问题核心。题目中
n的范围是1 ≤ n ≤ 39且为奇数。使用int c[41][41]在栈上声明数组,大小是足够的(因为 41 > 39)。所以,数组大小本身并不是导致“过不了”的直接原因。那么,问题可能出在哪里呢?我们来一起分析:
-
数组初始化问题:你的
int c[41][41]声明在main函数内部,是局部变量。局部数组不会自动初始化为0,其内容是未定义的随机值。而你的幻方填充逻辑中,判断c[a-1][b+1] == 0是核心条件。如果该位置原本是随机值而非0,你的填充顺序就会被打乱,导致结果错误。你换成vector后,通过vector<int>(n+2, 0)进行了显式初始化,这是正确的。 -
数组下标范围:你原来的
int c[41][41]是固定大小的。但在后续的循环中,你访问的下标是a-1,b+1等。当a=1时,a-1=0,你访问了c[0][...];当b=n时,b+1=n+1,你访问了c[...][n+1]。对于int c[41][41],如果n=39,n+1=40,仍在数组范围内(0~40)。但访问下标0在逻辑上可能不符合你的预期,不过C++语法上不会越界。然而,更关键的是,你的vector创建为n+2大小,这为边界情况(如n+1)预留了空间,这是一个好习惯。 -
代码逻辑的潜在问题:让我们仔细审视你的条件判断分支。题目描述的规则是:
- 若当前位置在第一行但不在最后一列,则下一个数填在最后一行、下一列。
- 若当前位置在最后一列但不在第一行,则下一个数填在上一行、第一列。
- 若当前位置在第一行最后一列,则下一个数填在当前位置的正下方。
- 若当前位置既不在第一行也不在最后一列,且右上方未填数,则填在右上方;否则填在正下方。
你的代码结构是:
if (a != 1 && b != n) { ... } else if (a == 1 && b != n) { ... } else if (a != 1 && b == n) { ... } else if (a == 1 && b == n) { ... }这个逻辑覆盖了所有情况,但顺序很重要。请思考:当
a==1 && b!=n时,进入第二个分支;当a!=1 && b==n时,进入第三个分支。这符合题目描述吗?题目说“第一行但不在最后一列”对应你的第二个分支,是正确的。但请再检查一下:你的第一个分支if (a != 1 && b != n)中,有一个内层判断if (c[a-1][b+1] == 0),这里a-1可能等于0吗?(当a=1时不会进入这个分支,所以安全)。然而,当a=2, b=38 (n=39)时,c[1][39]是右上方,但39是n吗?b=38不等于n=39,所以进入第一个分支。这看起来正确。
那么,如何验证问题所在? 我建议你进行以下调试:
- 如果你仍想使用
int c[41][41],请在其声明后立即用memset(c, 0, sizeof(c));进行清零。然后测试是否可以通过。 - 检查你的输出。对于小的
n(如n=3),你可以手动计算幻方结果,与你的程序输出对比。 - 关注边界:当
n=1时,你的程序如何运行?n/2+1对于整数除法是可行的吗?(1/2=0, 0+1=1,正确)。
启发式提问:
- 你认为未初始化的数组会对你的程序逻辑产生什么影响?
- 对于
n=3,你能手动模拟出正确的幻方吗?你的程序输出是否与之匹配? - 如果你使用
int c[41][41]并添加memset初始化,你认为结果会怎样?
请尝试上述调试步骤,并告诉我你的发现。如果问题仍然存在,我们可以进一步分析你的算法逻辑细节。记住,在竞赛编程中,局部数组的自动初始化问题是一个常见的陷阱。
-
- 1