1. 程式人生 > >HDU 2767 Proving Equivalences(至少增加多少條邊使得有向圖變成強連通圖)

HDU 2767 Proving Equivalences(至少增加多少條邊使得有向圖變成強連通圖)

Proving Equivalences

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


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  

 

Source NWERC 2008   分析: 給你一個有向圖,問你至少新增多少條邊,使得該圖變成一個強連通圖 特判情況:
m=0,沒有邊,那麼至少新增n條邊
sig=1,該圖本來就是強連通圖,則輸出0 tarjan求強連通分量,同時染色縮點得到新圖
統計新圖入度為0和出度為0的點的個數
輸出最大值,就是我們至少需要新增的邊的條數 原因:
縮點的原因:
強連通分量內部是互相可達的,我們只有把這些強連通分量縮成一個點,然後使得這些點構成的新圖變成強連通圖就可以了
所以問題得到關鍵是:
怎麼使得新圖變成強連通圖(新圖中不存在強連通分量,本身也不是強連通圖)
有向圖沒有構成環的話,肯定存在鏈
把鏈頭和鏈尾安裝某個方向連線起來
鏈就變成環了
所以看看鏈頭和鏈尾的個數就好(即出度為0和入度為0的點)
輸出鏈頭和鏈尾個數的最大值 為什麼是最大值?
因為如果連最小值條邊的話,新圖不一定能夠變成強連通圖   code:
#include<stdio.h>
#include<iostream>
#include<math.h>
#include<string.h>
#include<set>
#include<map>
#include<list>
#include<math.h>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long LL;
#define INF 0x7fffffff
int mon1[13]= {0,31,28,31,30,31,30,31,31,30,31,30,31};
int mon2[13]= {0,31,29,31,30,31,30,31,31,30,31,30,31};
int dir[4][2]= {{0,1},{0,-1},{1,0},{-1,0}};

int getval()
{
    int ret(0);
    char c;
    while((c=getchar())==' '||c=='\n'||c=='\r');
    ret=c-'0';
    while((c=getchar())!=' '&&c!='\n'&&c!='\r')
        ret=ret*10+c-'0';
    return ret;
}

#define max_v 20005
#define mem(a,x) memset(a,x,sizeof(a))
int vis[max_v];
int dfn[max_v];
int low[max_v];
int color[max_v];
int stk[max_v];
int indgree[max_v];
int outdgree[max_v];
vector<int> G[max_v];
int n,m;
int sig,cnt,sp;

void init()
{
    mem(indgree,0);
    mem(outdgree,0);
    mem(vis,0);
    mem(dfn,0);
    mem(low,0);
    mem(color,0);
    mem(stk,0);
    for(int i=1;i<=n;i++)
        G[i].clear();
    sig=0;
    cnt=1;
    sp=-1;
}

void tarjan(int u)
{
    vis[u]=1;
    low[u]=dfn[u]=cnt++;
    stk[++sp]=u;

    for(int j=0;j<G[u].size();j++)
    {
        int v=G[u][j];
        if(vis[v]==0)
            tarjan(v);
        if(vis[v]==1)
            low[u]=min(low[u],low[v]);
    }
    if(low[u]==dfn[u])
    {
        sig++;
        do
        {
            vis[stk[sp]]=-1;
            color[stk[sp]]=sig;
        }while(stk[sp--]!=u);
    }
}
int main()
{
    int t;
    int x,y;
    cin>>t;
    while(t--)
    {
        scanf("%d %d",&n,&m);
        if(m==0)
        {
            printf("%d\n",n);
            continue;
        }
        init();
        for(int i=1;i<=m;i++)
        {
            scanf("%d %d",&x,&y);
            if(x==y)
                continue;
            if(count(G[x].begin(),G[x].end(),y)==0)
                G[x].push_back(y);
        }

        for(int i=1;i<=n;i++)
        {
            if(vis[i]==0)
                tarjan(i);
        }

        if(sig==1)
        {
            printf("0\n");
            continue;
        }
       // printf("sig=%d\n",sig);
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<G[i].size();j++)
            {
                int v=G[i][j];
                if(color[i]!=color[v])
                {
                    indgree[color[v]]++;
                    outdgree[color[i]]++;
                }
            }
        }
        LL ans=0,ans1=0,ans2=0;
        for(int i=1;i<=sig;i++)
        {
            if(indgree[i]==0)
                ans1++;
            if(outdgree[i]==0)
                ans2++;
        }
        ans=max(ans1,ans2);
        printf("%d\n",ans);
    }
    return 0;
}
/*
給你一個有向圖,問你至少新增多少條邊,使得該圖變成一個強連通圖

特判情況:
m=0,沒有邊,那麼至少新增n條邊
sig=1,該圖本來就是強連通圖,則輸出0

tarjan求強連通分量,同時染色縮點得到新圖
統計新圖入度為0和出度為0的點的個數
輸出最大值,就是我們至少需要新增的邊的條數

原因:
縮點的原因:
強連通分量內部是互相可達的,我們只有把這些強連通分量縮成一個點,然後使得這些點構成的新圖變成強連通圖就可以了
所以問題得到關鍵是:
怎麼使得新圖變成強連通圖(新圖中不存在強連通分量,本身也不是強連通圖)
有向圖沒有構成環的話,肯定存在鏈
把鏈頭和鏈尾安裝某個方向連線起來
鏈就變成環了
所以看看鏈頭和鏈尾的個數就好(即出度為0和入度為0的點)
輸出鏈頭和鏈尾個數的最大值

為什麼是最大值?
因為如果連最小值條邊的話,新圖不一定能夠變成強連通圖

*/