@codeforces - 632F@ Magic Matrix


@description@

給定一個 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

@solution@

首先第 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))\)

@accepted code@

#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");
}

@details@

其實這道題還能夠用最小生成樹的思路解。
這樣的話,用 prim 求最小生成樹就是 O(n^2) 的,少個 log。
可是時限 5s 就不必了。

相關文章
相關標籤/搜索