Proving Equivalences(缩点+有环图变强连通分量)【tarjian算法】

Proving Equivalences

题目链接(点击)

参考博客(点击)

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 9497    Accepted Submission(s): 3349

Problem 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:

1. A is invertible.
2. Ax = b has exactly one solution for every n × 1 matrix b.
3. Ax = b is consistent for every n × 1 matrix b.
4. 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

思路:

题目搁了很久 昨天回头看题目的时候又看到了 想了好久没找到正确的方法 也没有思路 所以去看了看别人写的博客 虽然缩点和无环图变强连通图没有接触过 刚好学习了一下方法

缩点: 先通过tarjian算法把一个大的图分出来 好多强连通的小图 这样每个小图都有自己的编号(不会相同)

           然后缩点就是将每个小图看成一个新的小点 然后重新将这些点连接起来 构成一个 有向无环图(DAG)

           得到这个图之后通过判断 出、入度 就可以判断通添加几条边 可以让原来的大图变成前连通图 

出、入度:将开始输入的每个边都重新遍历 (这就需要在输入的时候 要将两个点一起存 方便遍历)

                 例如输入:

                              1 2

                              1 3

                 如果这条边的两个点在不同的小图中:

                 将 1对应小图的编号 的 入度 +=2(因为输入的两次都是以1开始)

                 将2、3对应小图的编号的出度分别 +=1;

下面举个例子:其中共6个点 输入以下8条边

    1 3

    1 2

    2 4

    3 4

    3 5

    4 6

    4 1

    5 6

① 通过tarjian算法可以将一个大图分为三个强连通小图 分表标号 1、2、3 如下:

(如果编号最大才是1 那么就表示 不需要再添加边 该图已经是强连通图)

e

② 遍历输入的边可以发现;

3 5

4 6 

5 6  这三条边满足小图编号不同

将3 4 5 对应的小图编号的入度+=1;

将5 6 6对应的小图编号的出度+=1;

③ 从1~3(编号最大为3) 遍历

分别判断入度和出度里面为0的个数

取个数最多的就是需要添加的边数使得该图变成强连通图 

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int MAX=5e4;
int n,m;
int ans;
int head[MAX+5];
struct node{
    int t;
    int to;
    int next;
}edge[MAX+5];

void addnode(int u,int v)
{
    edge[ans].t=u;
    edge[ans].to=v;
    edge[ans].next=head[u];
    head[u]=ans++;
}

void allbegin()
{
    memset(head,-1,sizeof(head));
    ans=0;
}
int num,top,sig;
int dfn[MAX+5],sta[MAX+5],low[MAX+5],scc[MAX+5];
int inde[MAX+5],outde[MAX+5];
void dfs(int u)
{
    dfn[u]=low[u]=++num;
    sta[top++]=u;
    for(int i=head[u];~i;i=edge[i].next){
        int t=edge[i].to;
        if(dfn[t]==0){
            dfs(t);
            low[u]=min(low[u],low[t]);
        }
        else if(scc[t]==0){
            low[u]=min(low[u],low[t]);
        }
    }
    if(dfn[u]==low[u]){
        sig++;
        while(1){
            int j=sta[--top];
            scc[j]=sig;
            if(j==u){
                break;
            }
        }
    }
}
void tarjian()
{
    sig=0,top=0,num=0;
    memset(inde,0,sizeof(inde));
    memset(outde,0,sizeof(outde));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(sta,0,sizeof(sta));
    memset(scc,0,sizeof(scc));
    for(int i=1;i<=n;i++){
        if(dfn[i]==0){
            dfs(i);
        }
    }
}
int main()
{
    int T;
    for(scanf("%d",&T);T--;){
        allbegin();
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            addnode(u,v);
        }
        tarjian();
        for(int i=0;i<m;i++){
            int u=scc[edge[i].t];
            int v=scc[edge[i].to];
            if(u!=v){
                inde[u]++;
                outde[v]++;
            }
        }
        int u=0,v=0;
        for(int i=1;i<=sig;i++){
            if(inde[i]==0){
                u++;
            }
            if(outde[i]==0){
                v++;
            }
        }
        int maxx=max(u,v);
        if(sig==1){          //如果编号最大才是1 那么就表示 不需要再添加边 该图已经是强连通图
            printf("0\n");
        }
        else{
            printf("%d\n",maxx);
        }
    }
    return 0;
}

 

posted @ 2019-03-06 20:41  XJHui  阅读(83)  评论(0编辑  收藏  举报