[NOIP2017模擬]建設圖

2017.10.24 T1 2007

樣例數據
輸入

7 7
1 2
2 3
3 4
2 5
4 5
5 6
5 7

輸出

2

分析:看到這道題我就知道它是個tarjan了,只是不知道求什麼,看樣例我以爲是求割點,出其他數據發現不對;然後覺得也許是割邊,發現也不對;想了半天之後,覺得tarjan剩下的就只有雙連通分量了,試了試縮點,嗯,變成了一棵樹,於是我就又建了幾棵樹,發現需要(葉子節點+1)/2條邊就可以把整棵樹變成雙連通圖(根節點如果只連了一條邊也算是葉子),然後興沖沖地打好了這道題,還檢查了很久,覺得這道題就是今天的得分支柱了。
然而,今天在linux下評測,我的tarjan多年來記dfn和low值的都是“index”,在linux中“index”是關鍵字,直接編譯錯誤,爆0……而在oj上的評測機是windows環境,AC……(其實還少了一個特判,如果整個圖都是雙連通的,那最後就只有一個根點,會被判成葉子節點,答案就是(1+1)/2=1,實際上並不需要連邊,但是不黑心的wuvin並沒有出這樣的數據卡我嘿嘿嘿)

結論:一棵樹要加邊成爲雙連通圖需要加(葉子節點數+1)/2條邊(如果根節點只連了一條邊也算作葉子節點)。

代碼

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;

int getint()
{
    int sum=0,f=1;
    char ch;
    for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
    if(ch=='-')
    {
        f=-1;
        ch=getchar();
    }
    for(;isdigit(ch);ch=getchar())
        sum=(sum<<3)+(sum<<1)+ch-48;
    return sum*f;
}

const int maxn=100010;
const int maxm=200010;
int n,m,ans;
int tot,first[maxn],nxt[maxm*2],to[maxm*2];
int dep,dfn[maxn],low[maxn],zhan[maxn],top,cnt,slt[maxn];
int num,chudu[maxn];
bool visit[maxn];

void addedge(int x,int y)
{
    tot++;
    nxt[tot]=first[x];
    first[x]=tot;
    to[tot]=y;
    tot++;
    nxt[tot]=first[y];
    first[y]=tot;
    to[tot]=x;
}

void tarjan(int u,int fro)
{
    dep++;//千萬不要再用index了!!!
    dfn[u]=dep;
    low[u]=dep;
    zhan[top]=u;
    top++;

    for(int p=first[u];p;p=nxt[p])
    {
        int v=to[p];
        if(!dfn[v])
        {
            tarjan(v,p);
            low[u]=min(low[u],low[v]);
        }
        else
            if((p^1)!=fro)
                low[u]=min(low[u],dfn[v]);
    }

    if(low[u]==dfn[u])//把雙連通分量縮點
    {
        cnt++;
        while(zhan[top]!=u)
        {
            top--;
            slt[zhan[top]]=cnt;
        }
    }
}

void dfs(int u)
{
    for(int p=first[u];p;p=nxt[p])
    {
        int v=to[p];
        if(!visit[v])
        {
            visit[v]=true;
            if(slt[v]!=slt[u])//不是同一個連通塊所以u的連通塊的出度++
                chudu[slt[u]]++;
            dfs(v);
        }
    }
}

int main()
{
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);

    int x,y;tot=1;
    n=getint(),m=getint();
    for(int i=1;i<=m;++i)
    {
        x=getint(),y=getint();
        addedge(x,y);
    }

    tarjan(1,0);//tarjan求雙連通分量並縮點
    visit[1]=true;
    dfs(1);//從根開始向深處搜,找到出度爲0的點,也就是葉子節點
    for(int i=1;i<=cnt;++i)
        if(chudu[i]==0)
            num++;
    if(chudu[slt[1]]==1)//特判一下根節點是不是隻連了一條邊
        num++;

    if(num==1)//整個圖就只有一個點的話
        ans=0;
    else
        ans=(num+1)/2;//結論
    cout<<ans<<'\n';
    return 0;
}

本題結。

發佈了152 篇原創文章 · 獲贊 136 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章