hihocoder 1343 : Stable Members【拓扑排序】
hihocoder #1343:題目
解釋:
一個(gè)學(xué)習(xí)小組,一共有N個(gè)學(xué)員,一個(gè)主管。每個(gè)學(xué)員都有自己的導(dǎo)師(一個(gè)或者多個(gè)),導(dǎo)師可以是其他學(xué)員也可以是主管。
每周學(xué)員都要把自己的學(xué)習(xí)報(bào)告和收到的報(bào)告提交給自己的導(dǎo)師,這個(gè)團(tuán)隊(duì)設(shè)計(jì)很合理,沒(méi)有回環(huán)(投遞出去的報(bào)告不會(huì)回到自己手中),并且所有的報(bào)告最終都會(huì)投遞到主管那里。
但這個(gè)團(tuán)隊(duì)中有的學(xué)員會(huì)因?yàn)槠渌硞€(gè)學(xué)員不工作而導(dǎo)致報(bào)告無(wú)法提交到主管手中,我們稱這種學(xué)員為不可靠的。而不受某個(gè)學(xué)員不工作而影響到報(bào)告提交的就是可靠學(xué)員。
問(wèn)題就是找出可靠學(xué)員的數(shù)量。
輸入:
第一行數(shù)字是N,學(xué)員總數(shù)。接下來(lái)每行對(duì)應(yīng)1到N學(xué)員的導(dǎo)師數(shù)量和編號(hào),例如第二行輸入(1 0),代表學(xué)員1的導(dǎo)師有1個(gè),并且就是主管(0代表主管);第四行輸入(2 1 2),代表學(xué)員3的導(dǎo)師有兩個(gè),分別是學(xué)員1和2。
輸出:
可靠學(xué)員的數(shù)量
題意:一個(gè)有向無(wú)環(huán)圖,最上游的點(diǎn)只有一個(gè)。若刪掉一個(gè)點(diǎn),則某些后續(xù)點(diǎn)無(wú)法與最上游的點(diǎn)連通,則這些后續(xù)點(diǎn)為unstable的。要找出所有unstable的點(diǎn),然后輸出剩下的stable點(diǎn)的數(shù)量。
解法一:BFS拓?fù)?/strong>
對(duì)于每個(gè)頂點(diǎn)v,都遍歷其后續(xù)頂點(diǎn),找到所有的unstable的點(diǎn)。具體方法如下,采用染色的方法:
對(duì)于某個(gè)頂點(diǎn)v,采用拓?fù)渑判虻姆椒ū闅v其后續(xù)頂點(diǎn),并依次染色。后續(xù)的頂點(diǎn)進(jìn)入隊(duì)列的條件是,其所有的父頂點(diǎn)都已經(jīng)被染成頂點(diǎn)v的顏色。因?yàn)闀?huì)出現(xiàn)這樣的情況,當(dāng)刪掉1時(shí),雖然4的入邊有2條,但也是unstable的。
解法二: 記憶化搜索DFS【找每個(gè)學(xué)員的匯聚點(diǎn)】
思考一下,針對(duì)不穩(wěn)定的學(xué)員,他的導(dǎo)師路徑如果有多條必定會(huì)在某個(gè)時(shí)刻匯聚到同一個(gè)學(xué)員那里,而穩(wěn)定的學(xué)員匯聚點(diǎn)肯定是自身。
解釋:首先對(duì)于直接導(dǎo)師中就有主管的,那么匯聚點(diǎn)就是本身,因?yàn)楸旧砭褪欠€(wěn)定的。其次對(duì)于導(dǎo)師只有一個(gè)但不是主管的,迭代去找最靠近主管的匯聚點(diǎn)。最后對(duì)于有多個(gè)導(dǎo)師的情況,就需要分別迭代去找其導(dǎo)師的匯聚點(diǎn),如果其某兩個(gè)導(dǎo)師的匯聚點(diǎn)不同,那么他是穩(wěn)定的,他的匯聚點(diǎn)是自己。如果都一樣,那么匯聚點(diǎn)就是其導(dǎo)師們的匯聚點(diǎn)。
如樣例輸入中:
1、2的導(dǎo)師都是0,所以匯聚點(diǎn)是自己1與2。
3的導(dǎo)師有兩個(gè)1和2,他們的匯聚點(diǎn)分別是1,2,不同,那么3的匯聚點(diǎn)是3。
4的導(dǎo)師是3,3的匯聚點(diǎn)是3,那么4的匯聚點(diǎn)也是3。
5的導(dǎo)師4和3,匯聚點(diǎn)都是3,所以5的匯聚點(diǎn)也是3。
解法三:支配樹【必經(jīng)節(jié)點(diǎn),LCA】
這里用編號(hào)0來(lái)表示Master。此時(shí),“不穩(wěn)定成員”的定義就是:如果存在某一個(gè)編號(hào)不為0的點(diǎn),使得從點(diǎn)0到該點(diǎn)的所有路徑中都必須經(jīng)過(guò)這個(gè)點(diǎn),那么該點(diǎn)代表的成員就是“不穩(wěn)定成員”。上圖中,從點(diǎn)0到點(diǎn)4的所有路徑必經(jīng)過(guò)點(diǎn)3,因此成員4是不穩(wěn)定成員。
支配樹:
??? 將上面“不穩(wěn)定成員”的定義加以拓展,去掉“非0點(diǎn)”的限制,可以得到“支配點(diǎn)”的定義,即:對(duì)于某一個(gè)目標(biāo)點(diǎn),如果存在一個(gè)點(diǎn),使得圖中從起點(diǎn)到目標(biāo)點(diǎn)所有路徑都必須經(jīng)過(guò)該點(diǎn),那么該點(diǎn)就是目標(biāo)點(diǎn)的“支配點(diǎn)”。上圖中:點(diǎn)1、2、3、4的支配點(diǎn)分別為:0、0、0、0和3。顯然,如果從起點(diǎn)出發(fā)可以到達(dá)圖中的所有點(diǎn),那么起點(diǎn)就是圖中所有點(diǎn)的“支配點(diǎn)”。
??? 一個(gè)點(diǎn)的“支配點(diǎn)”不一定只有一個(gè)。例如:如果對(duì)于某個(gè)點(diǎn),從起點(diǎn)到該點(diǎn)的路徑只有1條,那么該路徑上的所有點(diǎn)都是該點(diǎn)的支配點(diǎn)。對(duì)于有多個(gè)支配點(diǎn)的情況,我們可以找到一個(gè)支配點(diǎn),它距離目標(biāo)點(diǎn)的最近,這個(gè)點(diǎn)我們成為“直接支配點(diǎn)”。對(duì)于給定的圖,一個(gè)點(diǎn)可能用有多個(gè)支配點(diǎn),但它的直接支配點(diǎn)一定是唯一的。此時(shí),出去起點(diǎn)外,所有的點(diǎn)都有自己唯一的“直接支配點(diǎn)”。
??? 而“支配樹”是這樣的一種多叉樹:圖中的點(diǎn)對(duì)應(yīng)于樹的節(jié)點(diǎn),而每一個(gè)節(jié)點(diǎn)的父節(jié)點(diǎn)則是它的直接支配點(diǎn)。上文中的圖構(gòu)成的支配樹如下:
??? 顯然,完成樹的構(gòu)建后,每個(gè)點(diǎn)的父節(jié)點(diǎn)就是它的直接支配點(diǎn),而這個(gè)點(diǎn)的所有祖先節(jié)點(diǎn)都是它的支配點(diǎn)。此時(shí),根據(jù)題意,我們要找的“穩(wěn)定成員”就是直接支配點(diǎn)是0號(hào)點(diǎn)(Master)的成員,也就是支配樹中根節(jié)點(diǎn)的孩子。
·建樹:
??? 為了建立支配樹,就必須知道每個(gè)點(diǎn)的直接支配點(diǎn)。考慮原圖中每個(gè)點(diǎn)的“前驅(qū)點(diǎn)”,本題中即考慮每個(gè)成員的mentor。如果某個(gè)成員只有一個(gè)mentor,那么顯然從Master到該成員的路徑一定都會(huì)經(jīng)過(guò)他的mentor,因此mentor就是該成員的直接支配點(diǎn);對(duì)于抽象的圖而言,如果某一個(gè)點(diǎn)只有一個(gè)前驅(qū)點(diǎn),那么該前驅(qū)點(diǎn)就是當(dāng)前點(diǎn)的直接支配點(diǎn);如果某個(gè)成員有多個(gè)mentor,那么對(duì)于某一個(gè)mentor而言,從Master到該成員就未必會(huì)經(jīng)過(guò)它,所以,當(dāng)某個(gè)成員擁有多個(gè)mentor時(shí),他的mentor都不是他的直接支配點(diǎn);對(duì)于抽象的圖而言,如果一個(gè)點(diǎn)有多個(gè)前驅(qū),那么這些前驅(qū)點(diǎn)都不是它的直接支配點(diǎn),我們需要考慮這些前驅(qū)節(jié)點(diǎn)的支配點(diǎn),當(dāng)這些前驅(qū)節(jié)點(diǎn)擁有共同的一個(gè)支配點(diǎn)時(shí),說(shuō)明從起點(diǎn)到這些前驅(qū)點(diǎn)的所有路徑必會(huì)經(jīng)過(guò)這個(gè)共有的支配點(diǎn),也就是說(shuō),從起點(diǎn)到目標(biāo)點(diǎn)的所有路徑都會(huì)經(jīng)過(guò)這個(gè)共有的支配點(diǎn),因此這個(gè)共有的支配點(diǎn)就是目標(biāo)點(diǎn)的直接支配點(diǎn)。這個(gè)結(jié)論對(duì)于只有一個(gè)前驅(qū)節(jié)點(diǎn)的情況也使用
??? 根據(jù)支配樹的定義,多個(gè)節(jié)點(diǎn)共有的支配點(diǎn)是明確的,就是他們的公共祖先,而我們要找的則是最近公共祖先。這個(gè)結(jié)論對(duì)于只有一個(gè)前驅(qū)節(jié)點(diǎn)的情況也使用,因?yàn)槿绻挥幸粋€(gè)點(diǎn),那么它的最近公共祖先就是它自己。
??? 于是,建立支配樹的過(guò)程就是:首先將起點(diǎn)加入到樹中,作為整個(gè)支配樹的根,然后對(duì)于每一個(gè)節(jié)點(diǎn),找到其所有前驅(qū)節(jié)點(diǎn)在支配樹中的最近公共祖先,這個(gè)祖先節(jié)點(diǎn)就是當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)。
·拓?fù)渑判?#xff1a;
??? 上面的建樹過(guò)程有一個(gè)條件必須要保證,即某個(gè)點(diǎn)要加入到樹中時(shí),必須確保它的所有前驅(qū)點(diǎn)已經(jīng)在樹中,這樣才可以找到這些點(diǎn)的最近公共祖先。因此,節(jié)點(diǎn)添加入樹中的順序是很重要的。我們可以通過(guò)拓?fù)渑判蛘业胶线m的順序,拓?fù)渑判虻慕Y(jié)果就是節(jié)點(diǎn)加入樹的順序。這樣就保證了前驅(qū)節(jié)點(diǎn)一定先于當(dāng)前節(jié)點(diǎn)加入到樹中。
·最近公共祖先:
??? 建樹的過(guò)程設(shè)計(jì)到了求多個(gè)節(jié)點(diǎn)的最近公共祖先。我們可以采用一種復(fù)雜度為O(lgn)的算法來(lái)求解它。考慮每個(gè)節(jié)點(diǎn)在樹中的高度,將高度小的節(jié)點(diǎn)沿著父節(jié)點(diǎn)指針向上移動(dòng),在所有節(jié)點(diǎn)的高度相同時(shí)再同時(shí)沿著父節(jié)點(diǎn)指針向上移動(dòng),當(dāng)所有的節(jié)點(diǎn)都到達(dá)同一個(gè)節(jié)點(diǎn)時(shí),這個(gè)終點(diǎn)就是這些節(jié)點(diǎn)的最近公共祖先。
??? 以上是本題的解答思路,完成建立支配樹后,統(tǒng)計(jì)一下根節(jié)點(diǎn)有多少個(gè)孩子,就是本題的答案。
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<vector> using namespace std; const int maxn = 100005; struct Edge { int to,next; }edge[maxn*10]; int n,cnt,ans,head[maxn],deg[maxn]; int dep[maxn],parent[maxn],tmp[15]; //tmp[i]表示第i個(gè)直接前驅(qū)回溯到的節(jié)點(diǎn) vector<int> g[maxn]; void addedge(int u,int v) { edge[cnt].to = v; edge[cnt].next = head[u]; head[u] = cnt++; } int LCA(int u) { int min_dep = -1; for(int i = 0; i < g[u].size(); i++) { int v = g[u][i]; tmp[i] = v; if(min_dep == -1 || min_dep > dep[v]) min_dep = dep[v]; } for(int i = 0; i < g[u].size(); i++) { while(dep[tmp[i]] > min_dep) tmp[i] = parent[tmp[i]]; } while(true) { int i; for(i = 1; i < g[u].size(); i++) if(tmp[i] != tmp[0]) break; if(i >= g[u].size()) break; for(int i = 0; i < g[u].size(); i++) tmp[i] = parent[tmp[i]]; } return tmp[0]; } void bfs() { queue<int> q; for(int i = 0; i <= n; i++) if(deg[i] == 0) q.push(i); while(!q.empty()) { int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; deg[v]--; if(deg[v] == 0) { parent[v] = LCA(v); dep[v] = dep[parent[v]] + 1; if(parent[v] == 0) ans++; q.push(v); } } } } int main() { while(scanf("%d",&n)!=EOF) { memset(head,-1,sizeof(head)); memset(deg,0,sizeof(deg)); for(int i = 0; i <= n; i++) g[i].clear(); for(int i = 1; i <= n; i++) { int k; scanf("%d",&k); for(int j = 1; j <= k; j++) { int u; scanf("%d",&u); addedge(u,i); //childg[i].push_back(u); //parentdeg[i]++; //in degree} } ans = 0; bfs(); printf("%d\n",ans); } return 0; }轉(zhuǎn)載于:https://www.cnblogs.com/demian/p/6536799.html
總結(jié)
以上是生活随笔為你收集整理的hihocoder 1343 : Stable Members【拓扑排序】的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 四则运算关于加括号的思路
- 下一篇: bzoj 2342: 双倍回文 回文自动