邊連通分量/縮點-POJ3177-Redundant Paths

用tarjan 的強連通分量增加了兩句話得到的求邊連通分量的方法

也就是因爲無向圖加了兩條邊之後有連回去的邊,所以要判斷下一個子節點是不是父親節點,其餘的和有向圖的強連通分量沒差。

這個算法就是記錄兩個時間戳,一個是index,一個是low。不斷dfs。然後通過子節點更新low 這樣把子節點更新完之後,屬於同一個邊連通分量(可以兩條路徑經過同一個點,但是不能經過通一條邊的叫做邊連通分量)的點都入棧了。然後當前時間戳即爲最小時間戳,此時退棧並記錄節點。

<pre name="code" class="cpp">stack<int>sta;
void init2()
{
    e = 0;
    memset(head,-1,sizeof(head));
}
struct p1
{
    int index,low;
}nar[SIZE_D];
int Dindex,scc;
int used[SIZE_D],insta[SIZE_D],belong[SIZE_D],num[SIZE_D],par[SIZE_D];
void inittarjan()
{
    Dindex = 0;
    memset(insta,0,sizeof(insta));
    scc = 0;
    memset(num,0,sizeof(num));
}

void tarjan(int ver)
{
    used[ver] = 1;
    nar[ver].index = Dindex;//記錄當前的時間戳
    nar[ver].low = Dindex;//記錄最早的時間戳


    Dindex++;//時間戳++;
    sta.push(ver);//當前節點入棧
    insta[ver] = 1;

    for (int i = head[ver]; i != -1; i = pra[i].next){
        int u = pra[i].to;
        if (used[u] == -1){//遍歷子節點。如果子節點沒有被訪問過,則dfs


par[u] = ver;

            tarjan(u);
            nar[ver].low = min(nar[ver].low, nar[u].low);//最早的時間戳更新
        }else{
            if (insta[u] == 1)//如果已經在棧內。更新當前的最早時間戳

if (u != par[ver])

                nar[ver].low = min(nar[ver].low, nar[u].low);
        }
    }
    if (nar[ver].low == nar[ver].index){//dfs結束後回到了最早的時間戳的那個頂點
        scc++;
        int w;
        do{
            w = sta.top();//出棧
            sta.pop();
            insta[w] = 0;
            belong[w] = scc;//記錄這個節點屬於哪個連通分量
            num[scc]++;//這個連通分量裏面的節點個數加一
        }while (w != ver);//直到取到當前節點爲止。
    }
}


題意:讓你最少加多少條邊可以沒有橋(就是去掉這條邊,就不連通了。這條邊就是橋),可能會有重邊

題解:縮點——用tarjan 的邊連通分量可以縮點。

然後結果就是(出度爲1的點的個數+1)/ 2

判斷每個節點是否和子節點屬於同一個連通分量,如果不屬於。那麼這兩個連通分量自加1,注意是強連通分量+1不是頂點+1

因爲是無向圖 所以出度爲2就是有向圖裏的出度爲1

這個重邊好坑啊!

思考重邊會不會對結果造成影響,然後讀入的時候要判斷一下有沒有重邊。引申,讀入還要判斷一下有沒有自環!!!(自己連到自己的節點上)

思考重邊會不會對結果造成影響,然後讀入的時候要判斷一下有沒有重邊。引申,讀入還要判斷一下有沒有自環!!!

思考重邊會不會對結果造成影響,然後讀入的時候要判斷一下有沒有重邊。引申,讀入還要判斷一下有沒有自環!!!

思考重邊會不會對結果造成影響,然後讀入的時候要判斷一下有沒有重邊。引申,讀入還要判斷一下有沒有自環!!!

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<stack>
#define SIZE_D 5005
#define SIZE_B 50005
using namespace std;
int e,head[SIZE_D];

struct pp
{
    int to,next;
}pra[SIZE_B];
void addedge(int x,int y)
{
    pra[e].to = y;
    pra[e].next = head[x];
    head[x] = e++;

    pra[e].to = x;
    pra[e].next = head[y];
    head[y] = e++;

}
int repeat(int x, int y)
{
    for (int i = head[x]; i != -1; i = pra[i].next){
        if (pra[i].to == y)
            return 0;
    }
    return 1;
}

int out[SIZE_D];

int D,B;

stack<int>sta;
void init2()
{
    e = 0;
    memset(head,-1,sizeof(head));
}
struct p1
{
    int index,low;
}nar[SIZE_D];
int Dindex,scc;
int used[SIZE_D],insta[SIZE_D],belong[SIZE_D],num[SIZE_D],par[SIZE_D];
void inittarjan()
{
    Dindex = 0;
    memset(insta,0,sizeof(insta));
    scc = 0;
    memset(num,0,sizeof(num));
}

void tarjan(int ver)
{
    used[ver] = 1;
    nar[ver].index = Dindex;//記錄當前的時間戳
    nar[ver].low = Dindex;//記錄最早的時間戳


    Dindex++;//時間戳++;
    sta.push(ver);//當前節點入棧
    insta[ver] = 1;

    for (int i = head[ver]; i != -1; i = pra[i].next){
        int u = pra[i].to;
        if (used[u] == -1){//遍歷子節點。如果子節點沒有被訪問過,則dfs


par[u] = ver;

            tarjan(u);
            nar[ver].low = min(nar[ver].low, nar[u].low);//最早的時間戳更新
        }else{
            if (insta[u] == 1)//如果已經在棧內。更新當前的最早時間戳

if (u != par[ver])

                nar[ver].low = min(nar[ver].low, nar[u].low);
        }
    }
    if (nar[ver].low == nar[ver].index){//dfs結束後回到了最早的時間戳的那個頂點
        scc++;
        int w;
        do{
            w = sta.top();//出棧
            sta.pop();
            insta[w] = 0;
            belong[w] = scc;//記錄這個節點屬於哪個連通分量
            num[scc]++;//這個連通分量裏面的節點個數加一
        }while (w != ver);//直到取到當前節點爲止。
    }
}


int main()
{
    //freopen("input.txt","r",stdin);
    while (~scanf("%d %d",&D,&B)){
        inittarjan();
        init2();
        for (int i = 1; i <= B; i++){
            int tempx,tempy;
            scanf("%d %d",&tempx,&tempy);
            if (repeat(tempx,tempy))
                addedge(tempx,tempy);
        }
        memset(used,-1,sizeof(used));
        for (int i = 1; i <= D; i++)
            if (used[i] == -1){
                par[i] = -1;
                tarjan(i);
            }
        //tarjan();
        int res = 0;
        memset(out,0,sizeof(out));
        for (int i = 1; i <= D; i++){//在別的題目裏面有可能不是1到D爲序號!!

            for (int j = head[i]; j != -1;j = pra[j].next){
                int u = pra[j].to;
                if (belong[pra[j].to] != belong[i]){//判斷每個節點是否和子節點屬於同一個連通分量,如果不屬於。那麼這兩個連通分量自加1,注意是強連通分量+1不是頂點+1
                    out[belong[pra[j].to]]++;
                    out[belong[i]]++;
                }
                //printf("delong[%d] = %d belong[%d] = %d\n",i,belong[i],u,belong[u]);
            }

        }
        //printf("%d\n",scc);
        for (int i =1; i <= scc; i++)
            if (out[i] == 2){//注意,因爲是無向圖 所以出度爲2就是有向圖裏的出度爲1
                //printf("i=%d\n",i);
                res++;
            }
        printf("%d\n",(res+1)/2);
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章