參考:http://hi.baidu.com/1093782566/blog/item/e5a0e9229913bd048b82a175.html
http://www.cppblog.com/IronOxide/archive/2010/08/16/123622.html?opt=admin
題目簡述:n頭奶牛,給出若干個歡迎關系a b,表示a歡迎b,歡迎關系是單向的,但是是可以傳遞的。另外每個奶牛都是歡迎他自己的。求出被所有的奶牛歡迎的奶牛的數目。
模型轉換:N個頂點的有向圖,有M條邊(N≤10000,M≤50000)。求一共有多少個點,滿足這樣的條件:所有其它的點都可以到達這個點。
首先,這個題的N和M都非常大,硬做是肯定不行的??紤]如果這個圖是一棵樹,那么問題就變的很簡單了,因為至多有一個點滿足條件,這個點滿足條件的充要條件是:這個點是樹中唯一的出度為0的點。
那么我們能否把圖轉化為樹呢?首先可以想到的是,如果圖中包含有環,那么就可以把這個環縮成一個點,因為環中的任意兩個點可以到達,環中所有的點具有相同的性質,即它們分別能到達的點集都是相同的,能夠到達它們的點集也是相同的??s點后的圖必無環,否則,可將環上所有點也縮成一個點,與極大強聯通分量矛盾。
那么是否只有環中的點才具有相同的性質呢?進一步的考慮,圖中的每一個極大強連通分支中的點都具有相同的性質。所以,如果把圖中的所有極大強連通分支求出后,就可以把圖收縮成一棵樹,問題就迎刃而解了。
預備知識:有向圖的強連通分量的求法,這個和求割點的算法差不多。
算法框架:對有向圖求強連通分量,然后找出所有獨立的強連通分量(所謂獨立,就是該連通分量里面的點到外面的點沒有通路,當然,連通分量外的點是可以有路到強連通分量內的點的),如果獨立的強連通分量的數目只有一個,那么,就輸出這個強連通分量內解的個數,否則輸出無解。只要找到縮點后的圖中無出度的點的個數,設為cnt, 若 cnt > 1 , 則必無滿足條件的點,因為一個出度為
零的點無法到達另一個出度為零的點;若cnt = 1 , 則該點所對應的強聯通分量的點的個數即為答案。
算法證明:
1:假設a和b都是最受歡迎的cow,那么,a歡迎b,而且b歡迎a,于是,a和b是屬于同一個連通分量內的點,所有,問題的解集構成一個強連通分量。
2:如果某個強連通分量內的點a到強連通分量外的點b有通路,因為b和a不是同一個強連通分量內的點,所以b到a一定沒有通路,那么a不被b歡迎,于是a所在的連通分量一定不是解集的那個連通分量。
3:如果存在兩個獨立的強連通分量a和b,那么a內的點和b內的點一定不能互相到達,那么,無論是a還是b都不是解集的那個連通分量,問題保證無解。
4:如果圖非連通,那么,至少存在兩個獨立的連通分量,問題一定無解。
[cpp]?view plaincopy
#include?<iostream>?? #include?<stack>?? #include?<cstring>?? using?namespace?std;?? ?? const?int?MAXN?=?10000?+?10;??????? const?int?MAXM?=?50000?+?10;??????? ?? ?? struct?EDGE?? {?? ?int?v;?????????????????????? ?int?next;??????????????????? };?? ?? stack<int>?s;?? EDGE?edge[MAXM];?? int?low[MAXN];??????????????? int?dfn[MAXN];??????????????? int?first[MAXN];??????????????? int?sccf[MAXN];?????????????? bool?ins[MAXN];?????????????? int?outdegree[MAXN];????????? int?index;??????????????????? int?scc;????????????????????? int?n,?m;?? ?? ?? void?Init()?? {?? ????scc?=?0;?? ????index?=?1;?? ????memset(low,?0,?sizeof(low));?? ????memset(dfn,?0,?sizeof(dfn));?? ????memset(ins,?false,?sizeof(ins));?? ????memset(sccf,?0,?sizeof(sccf));?? ????memset(first,?-1,?sizeof(first));?? }?? ?? void?Tarjan(int?u)?? {?? ????int?v;?? ????low[u]?=?dfn[u]?=?index++;?? ????s.push(u);?? ????ins[u]?=?true;?? ?????? ????for?(int?k=first[u];?k!=-1;?k=edge[k].next)?? ????{?? ????????v?=?edge[k].v;?? ????????if?(dfn[v]?==?0)?? ????????{?? ????????????Tarjan(v);?? ????????????low[u]=min(low[u],low[v]);?? ????????}?? ????????else?if?(ins[v])?? ????????{?? ????????????low[u]=min(low[u],dfn[v]);?? ????????}?? ????}?? ?????? ????if?(dfn[u]?==?low[u])?? ????{?? ????????scc++;?? ????????do?? ????????{?? ????????????v?=?s.top();?? ????????????s.pop();?? ????????????ins[v]?=?false;?? ????????????sccf[v]?=?scc;?? ????????}while?(u?!=?v);?? ????}?? }?? ?? ?? ?? ?? ?? ?? ?? int?GetSuperPopularNum()?? {?? ????int?u,?v;?? ????int?cnt?=?0;?????? ????int?ct[MAXN];?????? ?? ????memset(outdegree,?0,?sizeof(outdegree));?? ????memset(ct,?0,?sizeof(ct));?? ?? ?? ?? ?????? ????for?(u=1;?u<=n;?u++)?? ????{?? ????????ct[sccf[u]]++;?? ????????for?(int?k=first[u];?k!=-1;?k=edge[k].next)?? ????????{?? ?????????????? ????????????v?=?edge[k].v;?? ????????????if?(sccf[u]?!=?sccf[v])?? ????????????{?? ????????????????outdegree[sccf[u]]++;?? ????????????}?? ????????}?? ????}?? ?? ?????? ????for?(u=1;?u<=scc;?u++)?? ????{?? ????????if?(outdegree[u]?==?0)?? ????????{?? ????????????cnt++;?? ????????????v?=?u;?? ????????}?? ????}?? ?? ????return?(cnt?==?1)??ct[v]?:?0;?? }?? ?? ?? ?? int?main()?? {?? ????int?i,?u,?v;?? ????int?e?=?0;???????? ?? ?????? ????Init();?? ????cin?>>?n?>>?m;?? ????for?(i=0;?i<m;?i++)?? ????{?? ????????cin?>>?u?>>?v;?? ????????edge[e].v?=?v;?? ????????edge[e].next?=?first[u];?? ????????first[u]?=?e;?? ????????e++;?? ????}?? ?? ?????? ????for?(i=1;?i<=n;?i++)?? ????{?? ????????if?(dfn[i]?==?0)?? ????????{?? ????????????Tarjan(i);?? ????????}?? ????}?? ?? ?????? ????cout?<<?GetSuperPopularNum()?<<?endl;?? ?? ????return?0;?? }??
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀
總結
以上是生活随笔為你收集整理的POJ 2186 popular cow 有向图的强联通问题 Tarjan算法的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。