给定一个 n*n 的矩阵 A。spa
咱们称 A 是 magic 的,当且仅当:
(1)A 是对称的。
(2)A 的主对角线 \(a_{ii} = 0\)。
(3)对于每一组 (i, j, k) 知足 \(a_{ij} \le \max\{a_{ik}, a_{jk}\}\)。code
判断给出的 A 是否是 magic 的。排序
Input
第一行一个整数 n (1 ≤ n≤ 2500) 。
接下来 n 行包含 n 个整数,描述矩阵 A。
注意 A 不必定对称或者主对角线全为 0。
Output
若是是 magic 的,输出 "MAGIC";不然输出 "NOT MAGIC"。递归
Examples
Input
3
0 1 2
1 0 2
2 2 0
Output
MAGICip
Input
2
0 1
2 3
Output
NOT MAGICit
首先第 1, 2 个条件直接判。主要是考虑第 3 个条件。io
解决这道题有一步很关键:将矩阵 A 当作一个图 G 的邻接矩阵。
虽然隔壁的 zjx 大佬告诉我这个已是套路了,但是菜如我并不熟悉这种套路。class
考虑假如不知足第 3 个条件,就有 w(i, j) > w(i, k) 且 w(i, j) > w(k, j)。
即 (i, j) 与两个边权比它严格小的边造成了三元环。im
能够发现三元环这个条件限制太严格,不太好判断。
假如 (i, j) 与若干个边权比它严格小的边造成了环,设造成的环为 i -> p1 -> p2 -> ... -> j -> i。这个状况是否也不合法呢?
考虑边 (i, p2),若是 w(i, p2) >= w(i, j) 则 i -> p1 -> p2 -> i 自己就造成了不合法的三元环。
若是 w(i, p2) >= w(i, j),则能够把环缩减为 i -> p2 -> ... p -> j -> i。递归验证必然能够验证到三元环。
因此只要造成环就必定不合法。
那么算法就很明晰了。将边权排序,从小到大加入边。
若是加入到一条边造成了环,则不合法。
同种边权先查询后同时加入。
时间复杂度 \(O(n^2\log(n^2))\)。
#include <cstdio> #include <algorithm> using namespace std; const int MAXN = 2500; const int MAXM = MAXN*MAXN/2; struct edge{ int x, y, k; friend bool operator < (const edge &a, const edge &b) { return a.k < b.k; } }e[MAXM + 5]; int fa[MAXN + 5]; int find(int x) { return fa[x] = (fa[x] == x ? x : find(fa[x])); } void unite(int x, int y) { int fx = find(x), fy = find(y); if( fx != fy ) fa[fx] = fy; } int A[MAXN + 5][MAXN + 5]; int main() { int n, cnt = 0; scanf("%d", &n); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d", &A[i][j]); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { if( A[i][j] != A[j][i] ) { puts("NOT MAGIC"); return 0; } if( i == j && A[i][j] ) { puts("NOT MAGIC"); return 0; } } for(int i=1;i<=n;i++) { for(int j=1;j<i;j++) cnt++, e[cnt].k = A[i][j], e[cnt].x = i, e[cnt].y = j; fa[i] = i; } sort(e + 1, e + cnt + 1); e[cnt + 1].k = -1; for(int i=1;i<=cnt;i++) { int j = i; while( e[j+1].k == e[i].k ) j++; for(int k=i;k<=j;k++) if( find(e[k].x) == find(e[k].y) ) { puts("NOT MAGIC"); return 0; } for(int k=i;k<=j;k++) unite(e[k].x, e[k].y); i = j; } puts("MAGIC"); }
其实这道题还能够用最小生成树的思路解。
这样的话,用 prim 求最小生成树就是 O(n^2) 的,少个 log。
可是时限 5s 就不必了。