試題編號: | 201509-4 |
試題名稱: | 高速公路 |
時間限制: | 1.0s |
內存限制: | 256.0MB |
問題描述: |
問題描述 某國有n個城市,爲了使得城市間的交通更便利,該國國王打算在城市之間修一些高速公路,由於經費限制,國王打算第一階段先在部分城市之間修一些單向的高速公路。 輸入格式 輸入的第一行包含兩個整數n, m,分別表示城市和單向高速公路的數量。 輸出格式 輸出一行,包含一個整數,表示便利城市對的數量。 樣例輸入 5 5 樣例輸出 3 樣例說明
評測用例規模與約定 前30%的評測用例滿足1 ≤ n ≤ 100, 1 ≤ m ≤ 1000; |
這個題是典型的用Tarjan算法求強連通分量的題。
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<unordered_map>
#include<set>
#include<sstream>
using namespace std;
const int MAXN = 10005;
vector<int> adj[MAXN]; //鄰接表
int low[MAXN]; //low[u] = 5表示u節點能追溯到的最早的棧中節點的序號,初始值爲自己。
int dfn[MAXN] = {0}; //dfn[u] = 5表示對u節點的Tarjan函數遞歸是第5次
bool vis[MAXN] = {false}; //vis[u] = true表示u在棧中。
int index = 0;
stack<int> st;
int cityNum = 0; //便利城市對的數量
void Tarjan(int u){
low[u] = dfn[u] = ++index;
st.push(u);
vis[u] = true;
for(int i=0;i<adj[u].size();i++){
int v = adj[u][i];
if(!dfn[v]){ //如果v節點還沒有被訪問過
Tarjan(v);
low[u] = min(low[u],low[v]);
}else if(vis[v]){
low[u] = min(low[u],dfn[v]);
}
}
int num = 0; //記錄這個強連通分量包含的節點數
if(dfn[u] == low[u]){ //說明自己是強連通分量的根節點
while(u != st.top()){ //開始彈棧,直到棧頂元素是u
vis[st.top()] = false;
st.pop();
num++;
}
vis[st.top()] = false;
st.pop();
num++; //最後把u也彈出。
//因爲是強連通分量,所以分量中的每兩個節點都是便利城市對。用排列組合的公式得:
cityNum += (num*(num-1)/2); //(num*(num-1)/2)是當前強連通分量中的便利城市對數
//注意:強連通分量可能只包含一個節點,此時是沒有便利城市對的,但是不需要分
//情況討論,因爲num-1會變成0,相當於cityNum += 0;
}
}
void TarjanTravel(int n){
for(int i=1;i<=n;i++){
if(!dfn[i])
Tarjan(i);
}
}
int main(){
int n,m;
int u,v;
cin>>n>>m;
while(m--){
cin>>u>>v;
adj[u].push_back(v);
}
TarjanTravel(n);
cout<<cityNum<<endl;
return 0;
}
另外:這個題是有向圖求強連通分量,還有一種題是無向圖求雙連通分量(連通塊)。
這裏有個例題,是洛谷上的,有興趣的小夥伴可以看一看,題目較難,也是Tarjan算法。
題目出處:https://www.luogu.com.cn/problem/P3225
CSDN博客解析:https://blog.csdn.net/qq_36915686/article/details/104677266