洛谷Oj-P1726 上白澤慧音-強連通分量
阿新 • • 發佈:2019-02-03
問題描述:
在幻想鄉,上白澤慧音是以知識淵博聞名的老師。春雪異變導致人間之裡的很多道路都被大雪堵塞,使有的學生不能順利地到達慧音所在的村莊。因此慧音決定換一個能夠聚集最多人數的村莊作為新的教學地點。人間之裡由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;
}
解決方法:
大小最大?字典序最小?由於每個點的編號不相同,只需要一個強連通分量裡最小的點編號最小即字典序最小。
強連通分量