构造不会一点。
填数游戏(241215 热身赛)
有一个 \(n\times m\) 的矩阵,将 \(1,2,\cdots,n\times m\) 填入矩阵中,满足:
- 每个位置恰好填入一个数字;
- \(1,2,\cdots,n\times m\) 中每个数字恰好出现一次;
- 矩阵每一行数字的和都不是质数,每一列数字的和都不是质数。
输出构造的 \(n\times m\) 矩阵,无解输出 \(\texttt{-1}\)。
\(1\le n,m\le1000\)。
首先考虑简单的情况:若 \(n=1\) 或 \(m=1\) 则无解,但是 \(n=m=1\) 时有解,即输出一个 \(\texttt1\)。
另外,显然 \(n,m\) 的顺序不影响答案,只需考虑 \(n<m\) 的情况,\(n>m\) 的情况输出转置即可。
观察所给的样例:\(n=2,m=3\),构造矩阵 \(\begin{pmatrix}1&2&3\\5&4&6\end{pmatrix}\)。猜想这样的构造方式是把原本自然排列的矩阵 \(\begin{pmatrix}1&2&3\\4&5&6\end{pmatrix}\) 稍微调整得来的。先考虑 \(n=2\) 的情况。
容易想到,如果让所有列的和都是偶数,那么就可以满足列的条件了。而偶数 = (偶 + 偶) 或者 (奇 + 奇),因此把上下的奇偶错开即可。对于最后一列,分别是 \(m\) 和 \(2m\),因此不用动。
进一步地,如果 \(m\) 是偶数,那么第一行:奇偶奇偶…,第二行:奇偶奇偶…,因此直接输出 \(\begin{pmatrix}1&2&\cdots &m\\m+1&m+2&\cdots&2m\end{pmatrix}\) 即可。
如果 \(m\) 是奇数,正如上面所说,最后一列不用调整,只需调整前 \(m-1\) 列,那么将
- 第一行:奇偶奇偶…奇偶 \(m\),
- 第二行:偶奇偶奇…偶奇 \(2m\)
改为
- 第一行:奇偶奇偶…奇偶 \(m\),
- 第二行:奇偶奇偶…奇偶 \(2m\)
即可。
对于 \(n>2\) 的情况,输出自然排列即可,这是因为第 \(i\) 行的和 \(\dfrac12m(m+1)\cdot i\) 和第 \(j\) 列的和
一定都是合数。
代码:
点击查看代码
#include <bits/stdc++.h>
#define f(x, y, z) for (int x = (y); x <= (z); ++x)
using namespace std;
int const N = 1e3 + 10;
int n, m, a[N][N];signed main() {cin >> n >> m;if (n == 1 && m == 1) return cout << "1\n", 0;if (n == 1 || m == 1 || (n == 2 && m == 2)) return cout << "-1\n", 0;int nn = max(n, m), mm = min(n, m);if ((n == 2 || m == 2) && (nn % 2 == 1)) {f(j, 1, nn) a[1][j] = j;f(j, 1, nn - 2) a[2][j] = nn + j + 1;a[2][nn - 1] = nn + 1;a[2][nn] = nn + nn;} else f(i, 1, mm) f(j, 1, nn) a[i][j] = nn * (i - 1) + j;if (n < m) f(i, 1, mm) {f(j, 1, nn) cout << a[i][j] << ' ';cout << '\n';} else f(i, 1, nn) {f(j, 1, mm) cout << a[j][i] << ' ';cout << '\n';}return 0;
}