1. 程式人生 > >洛谷Oj-P1726 上白澤慧音-強連通分量

洛谷Oj-P1726 上白澤慧音-強連通分量

問題描述:
在幻想鄉,上白澤慧音是以知識淵博聞名的老師。春雪異變導致人間之裡的很多道路都被大雪堵塞,使有的學生不能順利地到達慧音所在的村莊。因此慧音決定換一個能夠聚集最多人數的村莊作為新的教學地點。人間之裡由N個村莊(編號為1..N)和M條道路組成,道路分為兩種一種為單向通行的,一種為雙向通行的,分別用1和2來標記。如果存在由村莊A到達村莊B的通路,那麼我們認為可以從村莊A到達村莊B,記為(A,B)。當(A,B)和(B,A)同時滿足時,我們認為A,B是絕對連通的,記為

//鏈式前向星部分
struct edge
{
    int to;
    int next;
};
edge e[100010];
int
head[5010]; int cnt = 1; int n,m; int t = 1;//時間 int dfn[5010],low[5010];//時間戳,頂點i是DFS中被遍歷的第dfn[i]個頂點,low[i]表示頂點i所能直接或間接達到的dfn(時間戳)最小的頂點 bool book[5010];//標記頂點是否在棧中 stack<int> s; struct Scc//儲存連通分量 { int sum; vector<int> v; }; Scc scc[5010]; int id = 1; bool mark[5010];//是否被搜尋過 void add_edge(int u,int
v) { e[cnt].to = v; e[cnt].next = head[u]; head[u] = cnt; cnt++; } void Tarjan(int u) { dfn[u] = t;//打時間戳 low[u] = t;//設定low的初始值 t++;//時間+1s s.push(u);//將頂點u入棧 book[u] = true;//標記 for(int i = head[u]; i != -1; i = e[i].next) { int v = e[i].to;//終點 if(dfn[v] == 0
)//如果沒在棧中 { Tarjan(v);//繼續搜尋 low[u] = min(low[u],low[v]);//回溯時更新low } else//如果頂點to被搜尋過且還在棧中 { if(book[v] == true) low[u] = min(low[u],dfn[v]);//更新low } } if(dfn[u] == low[u])//如果頂點u是強連通分量對應子樹的根 { bool br = false;//是否應該break while(true) { int top = s.top();//取出 if(top == u)//如果取到了頂點u br = true; scc[id].sum++;//頂點數+1 mark[top] = true;//標記 scc[id].v.push_back(top);//加入陣列 s.pop();//出棧 book[top] = false;//取消在棧中的標記 if(br == true) break; } id++;//移動下標 } return; } int main() { int n,m; cin >> n >> m; memset(head,-1,sizeof(head));//初始化 for(int i = 1; i <= m; ++i)//建圖 { int a,b,k; cin >> a >> b >> k; if(k == 1) add_edge(a,b); if(k == 2) { add_edge(a,b); add_edge(b,a); } } for(int i = 1; i <= n; ++i)//防止圖不連通的情況 if(mark[i] == false) Tarjan(i); int max_scc = -inf;//找出強連通分量 int j; for(int i = 1; i <= id - 1; ++i) { if(scc[i].sum > max_scc) { max_scc = scc[i].sum; j = i; } } sort(scc[j].v.begin(),scc[j].v.end());//將頂點由小到大排序 cout << scc[j].sum << endl; int sz = scc[j].v.size(); for(int i = 0; i <= sz - 1; ++i) printf("%d ",scc[j].v[i]); return 0; }

解決方法:
大小最大?字典序最小?由於每個點的編號不相同,只需要一個強連通分量裡最小的點編號最小即字典序最小。
強連通分量