luooj P1226 修桥

题目链接
分析
双联通割边,依旧缩点,好像并没有用到桥的数量,不过作为一道模版题还是打了。详细见我注释的代码

#include<bits/stdc++.h>
#define N 5005
#define M 20005
using namespace std;
int tot,head[N],Next[M],vet[M];
void add(int x,int y){
    tot++;
    vet[tot]=y;
    Next[tot]=head[x];
    head[x]=tot;
}//边表连边
stack <int> s;
int m,n,ans,bcc,bcc_cnt,bccno[N],in[N];
int dfs_clock,low[N],dfn[N];
void Tarjan(int u,int fa){
    int cnt=0;s.push(u);
    dfn[u]=low[u]=++dfs_clock;
    for(int i=head[u];i;i=Next[i]){
        int v=vet[i];
        if(v==fa && ++cnt==1)continue;//此处用于判断连边是否有回边,就是说在连边时本来就有一条回边,但是题目可能还有别的回边。 
        if(!dfn[v]){
            Tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>dfn[u]) bcc++;//画图找规律,就发现这就是桥的数量
        }else
            if(dfn[v]<dfn[u])low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u]){
        bcc_cnt++;
        for(;;){
            int v=s.top();s.pop();
            bccno[v]=bcc_cnt;
            if(u==v)break;
        }
    }//另外和强联通分量的模版基本一样
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i])Tarjan(i,-1);
    for(int u=1;u<=n;u++)
        for(int i=head[u];i;i=Next[i]){
            int v=vet[i];
            if(bccno[u]!=bccno[v])
            in[bccno[v]]++;
        }//记录缩点后的入度
    for(int i=1;i<=bcc_cnt;i++)
        if(in[i]==1)ans++;
    printf("%d",(ans+1)/2);//根节点数+1/2是连边数。
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章