1 条题解

  • 0
    @ 2025-12-11 17:36:38

    好的,我们先来一步步分析这个问题,然后解释你给的代码在做什么。

    题目理解

    我们有 ( n ) 种方块,第 ( i ) 种方块有 ( i ) 个,长宽为 1(可以看作一个单元格放一个数字 ( i ))。

    我们要把所有数字 ( i ) 放到一个 ( r \times c ) 的矩形里,满足:

    1. 连通性:对于每种数字 ( i )(( 1 < i < n )),它的所有 ( i ) 个单元格在矩形中构成一个四连通块(上下左右相邻)。
    2. 不共线:对于 ( 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(偶数):

    1. ff=1,n-- 变成 3。
    2. 初始矩阵是 n=3 的矩阵(h=2, l=3)。
    3. 因为 n=3,i=4 循环不执行。
    4. ff=1,所以在右边加两列全填 4(n+1=4)。
    5. 输出 h=2, l=5 的矩阵,数字 4 有 4 个,满足。

    例如 n=5(奇数):

    1. 从初始 h=2, l=3(n=3 矩阵)开始。
    2. i=4 循环一次,构造数字 4 和 5,得到 h=3, l=5 矩阵。
    3. 输出。

    核心构造思想

    1. 利用奇偶分组构造:每次处理两个连续数字 i(偶数)和 i+1(奇数),确保它们各自连通且不共线。
    2. 矩阵逐渐加宽:每次加两列,增加一行,可以容纳新的数字并保持旧数字的连通性。
    3. 偶数 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
    上传者