CCF 高速公路 100分

試題編號: 201509-4
試題名稱: 高速公路
時間限制: 1.0s
內存限制: 256.0MB
問題描述:

問題描述

  某國有n個城市,爲了使得城市間的交通更便利,該國國王打算在城市之間修一些高速公路,由於經費限制,國王打算第一階段先在部分城市之間修一些單向的高速公路。
  現在,大臣們幫國王擬了一個修高速公路的計劃。看了計劃後,國王發現,有些城市之間可以通過高速公路直接(不經過其他城市)或間接(經過一個或多個其他城市)到達,而有的卻不能。如果城市A可以通過高速公路到達城市B,而且城市B也可以通過高速公路到達城市A,則這兩個城市被稱爲便利城市對。
  國王想知道,在大臣們給他的計劃中,有多少個便利城市對。

輸入格式

  輸入的第一行包含兩個整數n, m,分別表示城市和單向高速公路的數量。
  接下來m行,每行兩個整數a, b,表示城市a有一條單向的高速公路連向城市b

輸出格式

  輸出一行,包含一個整數,表示便利城市對的數量。

樣例輸入

5 5
1 2
2 3
3 4
4 2
3 5

樣例輸出

3

樣例說明


  城市間的連接如圖所示。有3個便利城市對,它們分別是(2, 3), (2, 4), (3, 4),請注意(2, 3)和(3, 2)看成同一個便利城市對。

評測用例規模與約定

  前30%的評測用例滿足1 ≤ n ≤ 100, 1 ≤ m ≤ 1000;
  前60%的評測用例滿足1 ≤ n ≤ 1000, 1 ≤ m ≤ 10000;
  所有評測用例滿足1 ≤ n ≤ 10000, 1 ≤ m ≤ 100000。

這個題是典型的用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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章