您的位置:首页 > 产品设计 > UI/UE

uva 12167 Proving Equivalences(强连通分量 + 缩点)

2015-09-15 21:10 453 查看

uva 12167 Proving Equivalences

Description

Consider the following exercise, found in a generic linear algebra textbook.

Let A be an n × n matrix. Prove that the following statements are equivalent:

A is invertible.

Ax = b has exactly one solution for every n × 1 matrix b.

Ax = b is consistent for every n × 1 matrix b.

Ax = 0 has only the trivial solution x = 0.

The typical way to solve such an exercise is to show a series of implications. For instance, one can proceed by showing that (a) implies (b), that (b) implies (c), that (c) implies (d), and finally that (d) implies (a). These four implications show that the four statements are equivalent.

Another way would be to show that (a) is equivalent to (b) (by proving that (a) implies (b) and that (b) implies (a)), that (b) is equivalent to (c), and that (c) is equivalent to (d). However, this way requires proving six implications, which is clearly a lot more work than just proving four implications!

I have been given some similar tasks, and have already started proving some implications. Now I wonder, how many more implications do I have to prove? Can you help me determine this?

Input

On the first line one positive number: the number of testcases, at most 100. After that per testcase:

One line containing two integers n (1 ≤ n ≤ 20000) and m (0 ≤ m ≤ 50000): the number of statements and the number of implications that have already been proved.

m lines with two integers s1 and s2 (1 ≤ s1, s2 ≤ n and s1 ≠ s2) each, indicating that it has been proved that statement s1 implies statement s2.

Output

Per testcase:

One line with the minimum number of additional implications that need to be proved in order to prove that all statements are equivalent.

Sample Input

2

4 0

3 2

1 2

1 3

Sample Output

4

2

题目大意:有四个命题a,b,c,d,我们证明我们证明他们的等价性需要进行:a←→b,然后b←→c,最后c←→d,一共需要6次推导。但四个命题的最小推导次数是:a→b, b→c,c→d,d→a只需四次推导。所以现在给出命题数量,以及已经完成的推导,问最少还需要几次推导可以完成这些命题的等价性证明。

解题思路:把命题看做结点,推导看做边,先求出原图的强连通分量。然后把每个强连通分量缩成点,就可以得到一个有向无环图(DAG)。然后判断缩点后图中入度为0的点和出度为0的点的个数,取其中大的那一个就是答案。注意,当强连通分量个数为1时,输出0。

[code]#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <queue>
#include <stack>
using namespace std;

typedef long long ll;
const int N = 20005;
int n, m;
vector<int> G
;
int pre
, lowlink
, sccno
, dfs_clock, scc_cnt;
int in0
, out0
;
stack<int> S;

void dfs(int u) {
    pre[u] = lowlink[u] = ++dfs_clock;  //pre[u]数组记录u点的访问时间
    S.push(u);
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];    
        if (!pre[v]) {
            dfs(v); 
            //lowlink[u]数组记录u点所可以到达的点中最早访问到的点的访问时间
            lowlink[u] = min(lowlink[u], lowlink[v]);
        } else if (!sccno[v]) { //v点还不属于其他强连通分量
            lowlink[u] = min(lowlink[u], pre[v]);
        }
    }
    if (lowlink[u] == pre[u]) { //u为该强连通分量的根
        //在整个强连通分量被找到时,统计强连通分量个数,以及给该强连通分量中的结点出栈并打上标记
        scc_cnt++;  
        for (;;) {
            int x = S.top(); S.pop();
            //sccno[x]记录x点所在强连通分量的编号
            sccno[x] = scc_cnt;
            if (x == u) break;
        }
    }
}

void find_scc(int n) {
    dfs_clock = scc_cnt = 0; //初始化
    memset(sccno, 0, sizeof(sccno));
    memset(pre, 0, sizeof(pre));
    for (int i = 0; i < n; i++) { //最外层循环,保证每个节点都被访问
        if (!pre[i]) dfs(i);
    }
}

void input() {
    scanf("%d %d", &n, &m);
    for (int i = 0; i <= n ;i++) {
        G[i].clear();   
    }
    for (int i = 0; i < m; i++) {
        int u, v;
        scanf("%d %d", &u, &v);
        u--, v--;
        G[u].push_back(v);
    }
}

void solve() {
    find_scc(n); //查找强连通分量
    for (int i = 1; i <= scc_cnt; i++) in0[i] = out0[i] = 1;
    for (int u = 0; u < n; u++) {
        for (int i = 0; i < G[u].size(); i++) {
            int v = G[u][i];    
            //统计入度或出度为零的强连通分量
            if (sccno[u] != sccno[v]) in0[sccno[v]] = out0[sccno[u]] = 0;
            //如果点u和点v在不同的强连通分量,但u到v有一条边,也就是说u所在的强连通分量到v所在的强连通分量有一条边,所以这两个强连通分量的入度或出度不为零
        }   
    }
    int a = 0, b = 0;
    for (int i = 1; i <= scc_cnt; i++) {
        if (in0[i]) a++;    
        if (out0[i]) b++;
    }
    int ans = max(a, b);
    if (scc_cnt == 1) ans = 0;
    printf("%d\n", ans);
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        input();        
        solve();
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: