1 条题解
-
0
好的,我们先来一步步分析这个问题,然后解释你给的代码在做什么。
题目理解
我们有 ( n ) 种方块,第 ( i ) 种方块有 ( i ) 个,长宽为 1(可以看作一个单元格放一个数字 ( i ))。
我们要把所有数字 ( i ) 放到一个 ( r \times c ) 的矩形里,满足:
- 连通性:对于每种数字 ( i )(( 1 < i < n )),它的所有 ( i ) 个单元格在矩形中构成一个四连通块(上下左右相邻)。
- 不共线:对于 ( i \ge 3 ),这些 ( i ) 的单元格不能全部在同一行或同一列。
注意数字 1 没有连通性要求(因为 ( 1 < i < n ) 才要求连通)。 数字 2 要有 2 个格子连通,并且不能全部在同一行或同一列(因为 ( i \ge 3 ) 才有不共线限制,所以数字 2 可以在同一行或列)。 数字 n 也没有连通性要求。
观察样例
题目给的样例 ( n=4 ) 输出(题目可能显示有点问题,我按代码逻辑推断):
可能输出是:
5 2 3 1 4 4 4 3 3 4 2 2看起来这个输出是 转置 显示的?
从代码输出来看,是先输出行数 ( h ) 和列数 ( l ),然后逐行输出数字。但为了符合题目,我们把它理解为:
- 行数 = 5,列数 = 2(即 5 行 2 列的矩阵)
- 数字 1 出现 1 次
- 数字 2 出现 2 次且连通(且不在同行同列?这里 2 可以同行同列,因为 ( i=2 ) 没说不共线)
- 数字 3 出现 3 次且连通,且不都在一行或一列(确实如此)
- 数字 4 出现 4 次(没有连通要求)
代码逻辑解析
你给出的代码没有输出 -1 的情况,意味着它认为对于所有 ( n ) 都有解,并且采用一种构造方法。
我逐步分析代码:
1. 小情况处理
if (n == 1) { puts("1 1\n1"); return 0; } if (n == 2) { puts("1 3\n1 2 2"); return 0; } if (n == 3) { puts("3 2\n3 3\n3 1\n2 2"); return 0; }n=1 时一个格子放 1。
n=2 时一维排列 1 2 2,2 的两个格子相邻连通。
n=3 时用一个 3 行 2 列矩阵,手动填好满足要求。2. 初始化
c[1][1] = 1; c[2][1] = c[2][2] = 2; c[1][2] = c[1][3] = c[2][3] = 3; int h = 2, l = 3, ff = 0;一开始构造了一个 2 行 3 列的矩阵:
行1:1 3 3
行2:2 2 3这里:
- 数字 1 有 1 个
- 数字 2 有 2 个(连通)
- 数字 3 有 3 个(连通且不在同一行/列)
此时 ( h=2, l=3 ) 对应 n=3 的情况已经做好了。
3. 主要构造(n ≥ 4)
if (!(n & 1)) ff = 1, n--;如果是偶数 n,先减 1 变成奇数,用 ff 标记最后要补回一个数字 n+1(即原来的 n)。
然后从 i=4 开始,每次增加 2:
for (int i = 4; i <= n; i += 2, l += 2) { h++; int t = h - i + l; // ... }循环内部:
- 每次列数增加 2(l += 2),高度增加 1(h++)。
- 数字 i 和 i+1 同时构造。
以 i=4(n 至少为 4)时为例,初始 h=2, l=3,做一次循环:
h=3, l=5, i=4, i+1=5
t = h - i + l = 3 - 4 + 5 = 4第一步:
for (int j = 1; j <= l + 1; j++) c[h][j] = i + 1; // 最后一行全填 5最后一行(第 3 行)前 6 列全填 5。
第二步:
for (int j = h - 1; j >= t; j--) c[j][l + 1] = i + 1; // 列 l+1 从某行到 h-1 填 5这里 t=4,但是 j 从 h-1=2 到 t=4 是空循环(2>=4 不成立),所以第二步暂时没作用。
第三步:
for (int j = 1; j <= t - 1; j++) c[j][l + 1] = i; // 列 l+1 前 t-1 行填 4前 3 行第 6 列填 4。
第四步:
for (int j = 1; j <= h; j++) c[j][l + 2] = i; // 列 l+2 整列填 4第 7 列整列填 4。
这样交错构造,使得数字 i 和 i+1 都满足:
- 数字 i 有 i 个,连通,且不共线(因为分布在两列或两行)。
- 数字 i+1 有 i+1 个,连通,且不共线。
4. 偶数 n 的处理
if (ff) { for (int j = 1; j <= h; j++) c[j][l + 1] = c[j][l + 2] = n + 1; l += 2; }如果原来 n 是偶数,我们刚才减了 1 变成奇数构造,现在增加一列(其实是两列)全填 n+1(即原来的 n),这样数字 n 就有 n 个。
因为 n 是偶数时,数字 n 没有连通性要求,所以直接整列填充即可。
5. 输出
printf("%d %d\n", h, l); for (int i = 1; i <= h; i++) { for (int j = 1; j <= l; j++) printf("%d ", c[i][j]); puts(""); }
举例验证
例如 n=4(偶数):
- ff=1,n-- 变成 3。
- 初始矩阵是 n=3 的矩阵(h=2, l=3)。
- 因为 n=3,i=4 循环不执行。
- ff=1,所以在右边加两列全填 4(n+1=4)。
- 输出 h=2, l=5 的矩阵,数字 4 有 4 个,满足。
例如 n=5(奇数):
- 从初始 h=2, l=3(n=3 矩阵)开始。
- i=4 循环一次,构造数字 4 和 5,得到 h=3, l=5 矩阵。
- 输出。
核心构造思想
- 利用奇偶分组构造:每次处理两个连续数字 i(偶数)和 i+1(奇数),确保它们各自连通且不共线。
- 矩阵逐渐加宽:每次加两列,增加一行,可以容纳新的数字并保持旧数字的连通性。
- 偶数 n 的处理技巧:先构造 n-1 的矩阵,再加一列全填 n,保证 n 的数量正确。
为什么这样可行?
- 对于每个 i (偶数),它分布在两列上,且数量=i,连通(因为是整列或部分列相连)。
- 对于每个 i+1 (奇数),它分布在两行上,数量=i+1,连通(整行或部分行相连)。
- 数字 2 和 3 已经在初始矩阵中处理好。
- 最后数字 1 始终在左上角,数字 n(若 n 是偶数)单独放在右边两列,不影响其他连通性。
这样构造保证了:
- 连通性:除了 1 和 n 外,每个数字的格子都连通。
- 不共线:数字 ≥3 的不会全在同一行/列。
- 矩阵尺寸控制在 ( O(n) \times O(n) ) 以内,满足数据范围 n ≤ 10000。
如果你愿意,我可以帮你画出 n=5 或 n=6 时构造矩阵的示意图,这样更直观。
- 1
信息
- ID
- 6097
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 4
- 已通过
- 1
- 上传者