海亮01/10构造专题
海亮01/10构造专题
T1
题意
给定一个长度为 \(n\) 的序列 \(a\),求 \(a\) 中的所有逆序对 \((i_1, j_1), (i_2, j_2), \cdots, (i_m, j_m)\) 的一个排列 \(p\),
使得依次交换 \((a_{i_{p_1}}, a_{j_{p_1}}), (a_{i_{p_2}}, a_{j_{p_2}}), \cdots, (a_{i_{p_m}}, a_{j_{p_m}})\) 后序列单调不降。
\(1 \le n \le 10^3\),\(1 \le a_i \le 10^9\)。
题解
先将原数组从小到大编号(如果两个数大小相同,那么按照下标从小到大编号)变成排列 \(b\)。
我们可以想到,每次考虑将最左边的位置 \(i\) 进行交换使得满足以下两条:
- 让位置 \(i\) 最终填上数字 \(i\)。
- 不改变剩下数字的相对位置。
然后问题就缩减成了 \([i+1,n]\)
然后你再想想,就会发现这玩应好像冒泡排序。
具体而言,每次需要填位置 \(i\),那么就从小到大(\(1\to i-1\))的交换满足 \(a_j>a_i\) 的 \(j\)。
但是但是,冒泡排序要求交换的是相邻的两个元素,怎么样才能交换不相邻的两个数呢?
然后发现有个神奇的东西叫做逆排列(设排列 \(p\),那么他的逆排列 \(q\) 需要满足 \(q_{p_i}=i\))
发现,如果 \((i,j)\) 在排列 \(p\) 中是逆序对,那么有 \(i<j,p_i>p_j\),而显然 \(q_{p_i}<q_{p_j},p_i>p_j\),也就是说,\((p_i,p_j)\) 在排列 \(q\) 中是逆序对(充要条件)。从小到大交换仍然满足刚刚的策略。
然后对着 \(q\) 冒泡排序即可,构造的话直接记录下来 \(q\) 的交换历程即可。
代码
#include<bits/stdc++.h> using namespace std; inline int read(){ int x = 0, f = 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } const int maxn = 1e3 + 10; int b[maxn], n; struct node{ int num, id, nid; node(int num = 0,int id = 0,int nid = 0):num(num),id(id),nid(nid){} }a[maxn]; struct cmp1{bool operator () (node a,node b){return a.num != b.num ? a.num < b.num : a.id < b.id;}}; struct cmp2{bool operator () (node a,node b){return a.id < b.id;}}; vector<pair<int,int> > ans; signed main(){ n = read(); for(int i = 1;i <= n;i++){a[i] = node(read(),i);} sort(a + 1,a + 1 + n,cmp1()); for(int i = 1;i <= n;i++)a[i].nid = i; sort(a + 1,a + 1 + n,cmp2()); for(int i = 1;i <= n;i++)b[a[i].nid] = i; // for(int i = 1;i <= n;i++)printf("%d ",b[i]);puts(""); for(int i = 1;i <= n;i++){ for(int j = 1;j < n;j++){ if(b[j] > b[j + 1]){ swap(b[j],b[j + 1]); ans.push_back(make_pair(b[j],b[j + 1])); } } } printf("%d\n",ans.size()); for(auto i : ans)printf("%d %d\n",i.first,i.second); return 0; }
T2
题意
给出一张无向连通图,选择以下任意一个任务完成:
- 找到图中一条至少包含 \(\lceil \frac {n} {2} \rceil\) 个点的简单路径。
- 找到图中偶数(至少 \(\lceil \frac {n} {2} \rceil\) )个点,且将它们两两配对。使满足任意两个点对包含的 \(4\) 个点的导出子图至多存在 \(2\) 条边。
其中,简单路径指不重复经过任意一个点的路径;导出子图指由给定点集与原图中两顶点均在给定点集中的边构成的图。
若完成任务 \(1\),则输出 PATH
,并输出简单路径包含的点数与该路径依次经过的点。
若完成任务 \(2\),则输出 PAIRING
,并输出选出的点对数及每一组点对。
可以证明,符合条件的一张图一定可以完成其中一个任务。
题解
先 dfs
生成树,然后发现整张图的边被分成了树边和返祖边两种。
然后分情况讨论。
- 如果有一个节点的深度超过了 \(\lceil\frac{n}2\rceil\),那么直接按着树边从这个点向根节点走即可。正确性显然。
- 否则整棵树的深度小于 \(\lceil\frac{n}2\rceil\),那么每一个深度的节点都两两配对,如果剩了一个节点就不要了,显然剩下的节点一定小于 \(\lceil\frac{n}2\rceil\),那么剩下的就一定大于 \(\lceil\frac{n}2\rceil\) 了。
- 正确性证明:
- 发现有两类 点对 的对:深度相同的和深度不相同的。
- 首先看每个点对内部的边数:
dfs
生成树没有横叉边,每个点对内部的边数为 \(0\)。 - 同理对于深度相同的 点对 的对,生成子图的边数为 \(0\)。
- 然后看深度不相同的 点对 的对。我们设两个点对分别为 \((x,y),(u,v),(dep_u<dep_x)\)。
- 我们知道 \(x,y\) 最多只有一个祖先(废话),那么显然点对 \((u,v)\) 对于 \(x\)(和 \(y\)) 的返祖边最多只有一条。然后加起来最多只有两条,就一定满足条件。
然后就没了。
代码
#include<bits/stdc++.h> using namespace std; inline int read(){ int x = 0, f = 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } const int maxn = 5e5 + 10, maxm = 1e6 + 10; int n, m; vector<int> edg[maxn]; int dep[maxn], fa[maxn]; vector<int> vec[maxn]; void dfs(int u,int f){for(int v : edg[u]){if(!dep[v]){dep[v] = dep[u] + 1;fa[v] = u;dfs(v, u);}}} stack<int> stk; vector<pair<int,int> > ans; void solve(){ n = read(); m = read();int u, v; for(int i = 1;i <= n;i++){dep[i] = 0;edg[i].clear();fa[i] = 0;vec[i].clear();} for(int i = 1;i <= m;i++){ u = read(); v = read(); edg[u].push_back(v);edg[v].push_back(u); } dep[1] = 1;dfs(1, 1); while(!stk.empty())stk.pop();ans.clear(); for(int i = 1;i <= n;i++){ if(dep[i] >= (n >> 1) + (n & 1)){ puts("PATH"); u = i;while(u != 1){stk.push(u);u = fa[u];} stk.push(1);printf("%d\n",stk.size()); while(!stk.empty()){printf("%d ",stk.top());stk.pop();} return; } vec[dep[i]].push_back(i); } puts("PAIRING"); for(int i = 1;i <= n;i++) for(int j = 0;j + 1 < vec[i].size();j += 2) ans.push_back(make_pair(vec[i][j],vec[i][j + 1])); printf("%d\n",ans.size()); for(auto i : ans)printf("%d %d\n",i.first,i.second); } signed main(){ int T = read(); while(T--)solve(); return 0; }
T3
题意
给你\(n\)个整数\(a_1 \,,a_2 \dots a_n\),第\(i\)个整数\(a_i\)满足\(i-n\le a_i \le i-1\).
找到一个这些整数的一个非空子集,使得它们的和为0。可以证明在给出的条件下一定存在这样的子集。如果有多个子集满足条件,输出其中一个。
题解
非常nb的一道题。
发现这个限制很神奇对叭,转化一下式子:
然后尝试将 \(i\to i-a_i\)(当然反过来也行,只要所有的 \(i\) 连的方向一致即可)
然后发现每一个点一定有且只有一个出度,然后变成奇环内向树,然后找到一个环(设其中的点的集合是 \(S\)),发现其中有着 \( \sum_{j\in S}j=\sum_{j\in S}(j-a_j) \iff\sum_{j\in S}j=\sum_{j\in S}j - \sum_{j\in S}a_j \iff \sum_{j\in S}a_j=0 \) 的好性质。
然后就做完了。
代码
#include<bits/stdc++.h> using namespace std; inline int read(){ int x = 0, f = 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } const int maxn = 1e6 + 10; int n, a[maxn]; vector<int> edg[maxn]; bool book[maxn]; vector<int> ans;bool flag; stack<int> stk; void dfs(int u){ if(flag)return; book[u] = 1;stk.push(u); for(int v : edg[u]){ if(book[v]){ while(stk.top() != v){ans.push_back(stk.top());stk.pop();} ans.push_back(v);flag = 1;return; } else dfs(v); if(flag)return; } } void solve(){ n = read(); for(int i = 1;i <= n;i++){edg[i].clear();a[i] = read();edg[i].push_back(i - a[i]);book[i] = 0;} ans.clear();while(!stk.empty())stk.pop(); flag = false;dfs(1); printf("%d\n",ans.size()); for(int i : ans)printf("%d ",i); puts(""); } signed main(){ int T = read(); while(T--){solve();} return 0; }
T4
题意
给出一个大小为 \(N \times M\) 的矩阵 \(a\),矩阵内的元素 \(\in [1 , N \times M]\),且两两不同
下面给出一些定义:
\(R_i\) 为第 \(i\) 行的元素集合,即\(R_i=\{a_{i,1},a_{i,2},...,a_{i,m}\}\)
\(C_j\) 为第 \(j\) 列的元素集合,即\(C_j=\{a_{1,j},a_{2,j},...,a_{n,j}\}\)
\(X\) 为矩阵每一行的最大元素的集合,\(Y\) 为矩阵每一列的最大元素的集合,即
\(X = \{\max(R_1),\max(R_2),..,\max(R_n)\}\)
\(Y = \{\max(C_1),\max(C_2),..,\max(C_m)\}\)
\(S(a) = (X , Y)\)
求构造一个新的 \(N \times M\) 的矩阵 \(a'\) 使其满足
- 矩阵内的元素 \(\in [1 , N \times M]\),且两两不同
- \(S(a') = S(a)\)
- 对于任意的行或列,使其满足先上升后下降,即存在 \(k\) 满足
行:\(a_{i,1}<a_{i,2}<...<a_{i,k}>a_{i,k+1}>...>a_{i,M}\)
列:\(a_{1,j}<a_{2,j}<...<a_{k,j}>a_{k+1,j}>...>a_{N,j}\)
如果存在多种合法构造,输出其中一种;若不存在,请输出 \(-1\)
题解
发现,如果一个数是列最大值,那么它必须新开出一个列出来,并且这个列所有数字都必须比他小,行同理。(如果同时是行和列最大值就同时新开一个行和列)
然后发现,我们从大到小的考虑一个数字填在哪里,我们设一个指针 \((x,y)\),那么分两种情况:
- 这个数是某一个行(或列、两者都是)的最大值,新开一行(或一列、一行一列)给它,并使指针 \(x\gets x+1\)(或 \(y\gets y+1\)、\(x\gets x+1,y\gets y+1\)),并且由于从大到小填入数字,那么显然新开的一行从 \(y-1\to 1\) 从大到小填入数字就是可以的(列、行列同理)。把这一段按顺序放入队列中。
- 否则,在队列中找到一个空闲的位置,填进去即可。
代码
#include<bits/stdc++.h> using namespace std; inline int read(){ int x = 0, f = 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } const int maxn = 250 + 10; int a[maxn][maxn]; int n, m; int mxn[maxn], mxm[maxn]; bool ml[maxn * maxn], mr[maxn * maxn]; queue<pair<int,int> > que; signed main(){ n = read(); m = read();int posx = 0, posy = 0; for(int i = 1;i <= n;i++){ for(int j = 1;j <= m;j++){ int x = read(); mxn[i] = max(x,mxn[i]); mxm[j] = max(x,mxm[j]); } } for(int i = 1;i <= n;i++)ml[mxn[i]] = 1; for(int i = 1;i <= m;i++)mr[mxm[i]] = 1; for(int i = n * m;i;i--){ if(ml[i] || mr[i]){ posx += ml[i];posy += mr[i]; a[posx][posy] = i; } else{a[que.front().first][que.front().second] = i;que.pop();} if(ml[i])for(int j = posy - 1;j;j--){que.push(make_pair(posx,j));} if(mr[i])for(int j = posx - 1;j;j--){que.push(make_pair(j,posy));} } for(int i = 1;i <= n;i++) for(int j = 1;j <= m;j++) printf("%d%c",a[i][j]," \n"[j == m]); return 0; }
T5
题意
给定参数\(n,k\)(\(1\le n \le 10^5 ,0\le k \le n\)),构造一棵二叉树,满足:
- 该二叉树有\(n\)个节点。
- 不存在只有1个儿子的节点。
- 对于一个非叶子节点,记他的两个儿子对应子树的大小为\(A,B\)。如果\(2A\le B\)或者\(2B \le A\),那么这个点是“不平衡点”。你构造的二叉树需要恰好有\(k\)个“不平衡点”。
如果无解,输出一行NO
;否则输出一行YES
,并在下一行输出每个节点的父亲编号(根节点输出0)表示你的构造。
题解
首先先想一个问题,就是如何用最少的点构造出来 \(k\) 个“不平衡点”?
显然的一个思路是让 \(1\to 2,1\to 3\),然后让 \(3\to 4,3\to 5\),以此类推,直到最后一个 \(2 \times k+1\to 2\times k+2,2\times k+1\to 2\times k + 3\),发现这里一共用了 \(2\times k+3\) 个点。
也就是说,如果 \(n<2\times k + 3\) 那么一定无解。
然后我们先让 \(2\times k+3\) 个点构造出来 \(k\) 个“不平衡点”,然后考虑剩下的 \(rest = n - 2\times k + 3\) 个点怎么办。
一个显然的思路是,把这些剩下的点放在第 \(2\times k + 1\) 点的下方构成完全二叉树。
这里发现另一个必要条件,就是 \(n\) 必须是奇数。
接着说,我们发现如果向下放节点构造出来的是一个满二叉树,皆大欢喜完结散花了对叭?
那如果不是满二叉树怎么办?
发现一个很好的性质就是这个完全二叉树只有一个“不平衡点”,并且如果再挂两个节点不是满二叉树,那么满二叉树不会增多新的“不平衡点”,否则减少一个“不平衡点”。
这个时候我们发现,可以将最上面的 \(1,2\) 两个节点“摘”下来,尝试放在完全二叉树上,如果仍然不是满二叉树就散花了。
如果还不是呢?
考虑能不能放在 \(4\) 节点的下方。
能够放当且仅当 \(k\ge 2\) 且放上之后 \(3\) 节点仍然是“不平衡点”。整理一下就是 \(n-4>6\) 且 \(k>1\)。
如果这两种情况都不行,那就没有其他的办法了,直接无解。
没了,可能写起来有点麻烦(
代码
#include<bits/stdc++.h> using namespace std; inline int read(){ int x = 0, f = 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } const int maxn = 1e5 + 10; int n, k; int fa[maxn]; void build(int u,int root){ if(u > n)return; int ls = ((u - root) << 1) + root, rs = ((u - root) << 1 | 1) + root; if(ls <= n)fa[ls] = u;build(ls,root); if(rs <= n)fa[rs] = u;build(rs,root); } int cnt; void build(int u){ if(cnt == k)return;cnt++; fa[u + 1] = fa[u + 2] = u; build(u + 2); } void printans(int getans = 1){ if(!getans){puts("NO");} else{puts("YES");for(int i = 1;i <= n;i++)printf("%d%c",fa[i]," \n"[i == n]);} exit(0); } signed main(){ n = read(); k = read(); if(!(n & 1)){puts("NO");return 0;} if(k == 0){ if((1 << (int)log2(n + 1)) != n + 1){printans(0);} build(1,0);printans(); } if(2 * k + 3 > n){printans(0);} build(1);int rest = n - 2 * k - 3; build(2 * k + 1,2 * k); if((1 << (int)log2(rest + 4)) == rest + 4){printans();} if((n - 4) > 6 && k > 1){ fa[3] = 0;fa[1] = fa[2] = 4; printans(); } if(1 << (int)(log2(rest + 6)) != rest + 6){ fa[3] = 0;fa[1] = fa[2] = (n + 1 - k) / 2 + 2 * k; printans(); } printans(0); return 0; }
T6
题意
给定 \(h\) 条水平线段,长度依次为 \(l_1,l_2,\ldots,l_h\) 和 \(v\) 条竖直线段 \(p_1,p_2,\ldots,p_v\),要求求出一种方案使竖直线段和水平线段交替首尾相接,且不在除端点外的其它地方有交点。
题解
发现,如果 \(n\neq m\) 肯定无解对叭?
然后尝试将水平线段分成 \(A,B\) 两个集合,使得 \(\sum_{i\in A}l_i=\sum_{i\in B}l_i\)。
同理将竖直线段分成 \(C,D\) 两个集合,使得 \(\sum_{i\in A}p_i=\sum_{i\in B}p_i\)。
如果没有方案无解。
然后这里钦定 \(|A|>|C|\)(让 \(A\) 是 \(A,B\) 中集合大小较大的那个, \(C\) 是 \(C,D\) 集合大小较小的那个,因为总和相同,所以一定能够通过交换满足条件)
然后发现将 \(A,B\) 从大到小排序,将 \(C,D\) 从小到大排序,然后让 \(A,C\) 成为使得坐标增大的那个,\(B,D\) 成为使得坐标减小的那个,然后发现图形就会变成一个近似三角形的形状,显然满足不相交的条件。
然后就没了。
代码
#include<bits/stdc++.h> using namespace std; inline int read(){ int x = 0, f = 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } const int maxn = 1e3 + 10; bitset<maxn * maxn> f[maxn]; bool work(int n,int *arr,bool *book){ int sum = 0; for(int i = 1;i <= n;i++)sum += arr[i]; if((sum & 1))return false; f[0].set(0); for(int i = 1;i <= n;i++) f[i] = f[i - 1] | (f[i - 1] << arr[i]); if(!f[n][sum / 2])return false; int x = sum / 2; for(int i = n;i;i--){ if(x >= arr[i] && f[i - 1][x - arr[i]]){ book[i] = 1;x -= arr[i]; } else if(f[i - 1][x]){book[i] = 0;} else return false; } return !x; } int n, m; int a[maxn], b[maxn]; bool book1[maxn], book2[maxn]; void solve(){ n = read();for(int i = 1;i <= n;i++){a[i] = read();book1[i] = 0;} bool check = work(n,a,book1);//printf("check = %d\n",check); m = read();for(int i = 1;i <= m;i++){b[i] = read();book2[i] = 0;} check &= work(m,b,book2);check &= (n == m);//printf("check=%d\n",check); if(!check){puts("No");return;} vector<int> v1, v2, v3, v4; v1.clear(); v2.clear(); v3.clear(); v4.clear(); for(int i = 1;i <= n;i++){ if(book1[i])v1.push_back(a[i]); else v2.push_back(a[i]); } for(int i = 1;i <= m;i++){ if(book2[i])v3.push_back(b[i]); else v4.push_back(b[i]); } if(v1.size() > v2.size())swap(v1,v2); if(v3.size() < v4.size())swap(v3,v4); sort(v1.begin(),v1.end(),greater<int>());sort(v2.begin(),v2.end(),greater<int>()); sort(v3.begin(),v3.end());sort(v4.begin(),v4.end()); // printf("%d %d %d %d\n",v1.size(),v2.size(),v3.size(),v4.size()); for(int i : v2){v1.push_back(-i);} for(int i : v4){v3.push_back(-i);} int x = 0, y = 0; vector<pair<int,int> > ans; for(int i = 0;i < (int)v1.size();i++){ x += v1[i]; ans.push_back(make_pair(x, y)); y += v3[i]; ans.push_back(make_pair(x, y)); } puts("Yes"); for(auto i : ans){printf("%d %d\n",i.first,i.second);} return; } signed main(){ int T = read(); while(T--){solve();} return 0; } /* 2 10 123 81 70 107 100 70 117 128 113 91 10 126 92 58 113 81 126 115 80 107 102 10 90 87 80 113 110 69 154 95 88 114 10 110 108 95 108 143 38 49 111 138 100 */
T11
题意
我们在棋盘上引入一个新的棋子,称之为“骆驼棋”。棋子跳跃规则:你可以将它移动到水平或垂直方向上的一个位置,使得新位置与旧位置之间隔着 \(2\) 格;或者像四个斜对角线的方向之一移动,使得新位置与旧位置之间恰好相隔一个格子。如图,棋盘的中心就是棋子的位置,\(8\) 个打上 ”\(\times\)“标记的就是可以移动到的位置。显然,棋子不可以跳出棋盘。
整个棋盘是 \(N\) 行 \(N\) 列的,其中保证 \(5 | N\) 。
一开始,棋子在棋盘的左上角。经过一系列移动,使得整个棋盘每一个格子都被走过恰好一次,并且最后的位置与开始位置之间恰好可以以骆驼棋一步互相到达,这就是所谓的”骆驼循环“。
你需要写一个程序,找到给定棋盘的”骆驼循环”。或者判断是否存在“骆驼循环”。
题解
发现可以构造出一个形如
1 | ||||
25 |
的矩阵,也就是可以用这样的矩阵连接向下(其他的方向旋转即可)。
然后可以构造出形如
1 | ||||
---|---|---|---|---|
0 | 0 | |||
25 |
的矩阵(\(0\) 是强制不选)作为出发点。
那么让 \(m=\frac{n}5\),然后分类讨论。
\(m\equiv0\pmod2\) 时:
不难发现可以构造形如:
1 | 36 | 35 | 34 | 33 | 32 |
---|---|---|---|---|---|
2 | 27 | 28 | 29 | 30 | 31 |
3 | 26 | 25 | 24 | 23 | 22 |
4 | 17 | 18 | 19 | 20 | 21 |
5 | 16 | 15 | 14 | 13 | 12 |
6 | 7 | 8 | 9 | 10 | 11 |
的循环,然后为 \(36\) 构造一个特殊的结束矩阵:
1 | ||||
25 | ||||
\(m\equiv 1\pmod2\) 时:
1 | 24 | 23 | 20 | 19 |
---|---|---|---|---|
2 | 25 | 22 | 21 | 18 |
3 | 14 | 15 | 16 | 17 |
4 | 13 | 12 | 11 | 10 |
5 | 6 | 7 | 8 | 9 |
然后为 \(25\) 构造一个特殊的矩阵:
25 | ||||
---|---|---|---|---|
1 | ||||
然后就没了,别忘了出发点强制在 \((1,1)\)。
代码
#include<bits/stdc++.h> using namespace std; inline int read(){ int x = 0, f = 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } const int maxn = 1e3 + 10; const int dx[8] = {-3,-2, 0, 2, 3, 2, 0,-2}; const int dy[8] = { 0, 2, 3, 2, 0,-2,-3,-2}; int a[10][10]; void dfs(int x,int y,int cnt){ if(cnt == 26){ if(a[3][3] != 25)return; for(int i = 1;i <= 5;i++) for(int j = 1;j <= 5;j++) printf("%c%2d%s",",{"[j == 1],a[i][j],j == 5 ? "},\n" : ""); puts(""); exit(0); } for(int i = 0;i < 8;i++){ int nx = x + dx[i], ny = y + dy[i]; if(nx < 1 || nx > 5 || ny < 1 || ny > 5 || a[nx][ny])continue; a[nx][ny] = cnt; dfs(nx,ny,cnt + 1); a[nx][ny] = 0; } } void preworkfunction(){ freopen("tmp.out","w",stdout); a[1][1] = 1;dfs(1,1,2); fclose(stdout); } int table[8][6][6] = { {//出发点(右上in中下out) { 1, 8, 5, 2, 9}, {16,13,22,19,14}, { 6, 3,10, 7, 4}, { 0,20,15, 0,21}, {17,12,23,18,11} }, {//奇数结束点(中左上in中心out) {25,12,19,24, 2}, {17, 7, 4,14, 8}, {20,23, 1,11,22}, { 5,13,18, 6, 3}, {16,10,21,15, 9} }, {//中心in左方out { 9,17,24, 8, 2}, {22,12, 4,19,13}, {25, 7, 1,16, 6}, {10,18,23,11, 3}, {21,15, 5,20,14} }, {//中心in上方out { 9,22,25, 8, 2}, {17,12, 4,20,15}, {24, 7, 1,23, 6}, {10,21,16,11, 3}, {18,13, 5,19,14} }, {//中心in右方out {22,14, 7,23, 2}, { 9,19, 4,12,18}, { 6,24, 1,15,25}, {21,13, 8,20, 3}, {10,16, 5,11,17} }, {//中心in下方out { 6,22,14, 7, 2}, {19, 9, 4,20,12}, {24,16, 1,23,15}, { 5,21,13, 8, 3}, {18,10,25,17,11} }, {//n=5特判 { 1,16,19, 2,15}, {11,22, 5,12,21}, {18, 8,25,17, 7}, { 4,13,20, 3,14}, {10,23, 6, 9,24} }, {//偶数结束点 {15, 5,11,14, 6}, {21,18, 8,24,19}, {10,13, 1, 4,12}, {16,25,20,17, 7}, {22, 3, 9,23, 2} } }; //0特殊st,1特殊ed,2左,3上,4右,5下 int n, m; int res[maxn / 5][maxn / 5][2];//[0]=typ,[1] = id void DEBUG(){ for(int i = 1;i <= m;i++) for(int j = 1;j <= m;j++) printf("%d%c",res[i][j][0]," \n"[j == m]); for(int i = 1;i <= m;i++) for(int j = 1;j <= m;j++) printf("%d%c",res[i][j][1]," \n"[j == m]); } int ans[maxn][maxn]; signed main(){ #ifndef ONLINE_JUDGE freopen("out.out","w",stdout); #endif // preworkfunction(); n = read();m = n / 5;int add1 = 0; if(m & 1){ if(m == 1){ for(int i = 1;i <= n;i++) for(int j = 1;j <= n;j++) printf("%d%c",table[6][i - 1][j - 1]," \n"[j == n]); return 0; } int x = 1, y = 1, id = 1; while(x <= m){res[x][y][0] = 5;res[x][y][1] = id;id++;x++;} id--;x--; while(1){ while(y <= m){res[x][y][0] = 4;res[x][y][1] = id;id++;y++;} y--;res[x][y][0] = 3;x--; if(x == 2)break; while(y >= 2){res[x][y][0] = 2;res[x][y][1] = id;id++;y--;} y++;res[x][y][0] = 3;x--; } for(int i = m;i >= 2;i--){ if(i & 1){ res[2][i][0] = 3; res[1][i][0] = 2; } else{ res[2][i][0] = 2; res[1][i][0] = 5; } res[2][i][1] = id + !(i & 1); res[1][i][1] = id + (i & 1); id += 2; } res[1][1][0] = 0;res[2][2][0] = 1; add1 = -2; // DEBUG(); } else{ int x = 1, y = 1, id = 1; while(x <= m){res[x][y][0] = 5;res[x][y][1] = id;id++;x++;} id--;x--; while(1){ while(y <= m){res[x][y][0] = 4;res[x][y][1] = id;id++;y++;} y--;res[x][y][0] = 3;x--; while(y >= 2){res[x][y][0] = 2;res[x][y][1] = id;id++;y--;} y++;res[x][y][0] = 3;x--; if(x == 0)break; } res[1][2][0] = 7;res[1][1][0] = 0; add1 = -2; } for(int i = 1;i <= m;i++){ for(int j = 1;j <= m;j++){ int x = (i - 1) * 5 + 1, y = (j - 1) * 5 + 1, add = (res[i][j][1] - 1) * 25; if(i != 1 || j != 1)add += add1; for(int q = 0;q < 5;q++){ for(int p = 0;p < 5;p++){ ans[x + q][y + p] = add + table[res[i][j][0]][q][p]; } } } } ans[4][1] = n * n;ans[4][4] = n * n - 1; for(int i = 1;i <= n;i++) for(int j = 1;j <= n;j++) printf("%d%c",ans[i][j]," \n"[j == n]); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】