CCF 高速公路 100分
试题编号: | 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
还没有评论,来说两句吧...