第六次CCF计算机软件能力认证考试(第四题)
2016-04-11 16:25
405 查看
问题描述
某国有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。
题目大意: 给定一个有向图,求出哪些点之间可以相互达到?最后输出的是,可以相互到达的点的对数
思路:这道题明显是求 强连通分量的个数,假设某个连通分量里面节点数为n,那么该连通分量的对数为 n*(n-1)/2 , 只要把所有连通分量的对数加起来就是整张图的对数
本题关键还是求 强连通分量的个数。我网上查了一下,决定用 Tarjan算法。
Tarjan算法:
核心还是dfs深度遍历;
用数组dfn[i]表示搜索到该节点所需要的时间戳、low[i]表示节点i直接或者间接达到的时间最小的点;
当u入栈,扫描u能够达到的所有节点,如果节点v没有访问过,那就先dfs遍历v, low[u] = min(low[u],low[v]) ,如果v在栈里,low[u] = min(low[u],dfn[v]); 当low[u] = dfn[u] 时,栈内节点u以及u以上所有的点全部出栈,这些点为一个强连通子图的节点。
以上时算法的实现步骤,关于证明,呃。。我也不太明白
#include <iostream>
#include <vector>
using namespace std;
#define MAX 10010
int n; //表示节点数目
vector<int> g[MAX]; //动态数组,节约空间
int Bcnt; //强连通分量个数
int Top; //栈顶
int Index; //时间戳
int low[MAX],dfn[MAX]; //Tarjan算法里的两个重要数组
int belong[MAX],stack[MAX]; //belong数组表示该节点属于哪一个连通分量
bool instack[MAX];
int answer = 0;
void Init_tarjan(){
Bcnt = Top = Index = 0;
for(int i=1;i<=n;i++)
low[i] = dfn[i] = 0;
}
void Tarjan(int u){
stack[Top++] = u; //将元素u入栈
instack[u] = 1; //标记元素已经入栈
low[u] = dfn[u] = ++Index;
for(int i=0;i<g[u].size();i++){
int v = g[u][i];
if(!dfn[v]){
Tarjan(v);
low[u] = min(low[v],low[u]);
}
else if(instack[v])
low[u] = min(low[u],dfn[v]);
}
if(low[u] == dfn[u]){
Bcnt++;
int sum = 0;
int v;
do{
v = stack[--Top]; //出栈
instack[v] = 0;
belong[v] = Bcnt;
sum++; //表示连通子图中节点的个数
}while(u!=v);
sum = (sum * (sum-1))/2;
answer = answer + sum;
}
}
int main(){
int m; //图中的边数
cin>>n>>m;
while(m--){
int x,y;
cin>>x>>y;
g[x].push_back(y);
}
Init_tarjan(); //先初始化
for(int i=1;i<=n;i++){
if(!dfn[i])
Tarjan(i);
}
cout<<answer<<endl;
return 0;
}
某国有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。
题目大意: 给定一个有向图,求出哪些点之间可以相互达到?最后输出的是,可以相互到达的点的对数
思路:这道题明显是求 强连通分量的个数,假设某个连通分量里面节点数为n,那么该连通分量的对数为 n*(n-1)/2 , 只要把所有连通分量的对数加起来就是整张图的对数
本题关键还是求 强连通分量的个数。我网上查了一下,决定用 Tarjan算法。
Tarjan算法:
核心还是dfs深度遍历;
用数组dfn[i]表示搜索到该节点所需要的时间戳、low[i]表示节点i直接或者间接达到的时间最小的点;
当u入栈,扫描u能够达到的所有节点,如果节点v没有访问过,那就先dfs遍历v, low[u] = min(low[u],low[v]) ,如果v在栈里,low[u] = min(low[u],dfn[v]); 当low[u] = dfn[u] 时,栈内节点u以及u以上所有的点全部出栈,这些点为一个强连通子图的节点。
以上时算法的实现步骤,关于证明,呃。。我也不太明白
#include <iostream>
#include <vector>
using namespace std;
#define MAX 10010
int n; //表示节点数目
vector<int> g[MAX]; //动态数组,节约空间
int Bcnt; //强连通分量个数
int Top; //栈顶
int Index; //时间戳
int low[MAX],dfn[MAX]; //Tarjan算法里的两个重要数组
int belong[MAX],stack[MAX]; //belong数组表示该节点属于哪一个连通分量
bool instack[MAX];
int answer = 0;
void Init_tarjan(){
Bcnt = Top = Index = 0;
for(int i=1;i<=n;i++)
low[i] = dfn[i] = 0;
}
void Tarjan(int u){
stack[Top++] = u; //将元素u入栈
instack[u] = 1; //标记元素已经入栈
low[u] = dfn[u] = ++Index;
for(int i=0;i<g[u].size();i++){
int v = g[u][i];
if(!dfn[v]){
Tarjan(v);
low[u] = min(low[v],low[u]);
}
else if(instack[v])
low[u] = min(low[u],dfn[v]);
}
if(low[u] == dfn[u]){
Bcnt++;
int sum = 0;
int v;
do{
v = stack[--Top]; //出栈
instack[v] = 0;
belong[v] = Bcnt;
sum++; //表示连通子图中节点的个数
}while(u!=v);
sum = (sum * (sum-1))/2;
answer = answer + sum;
}
}
int main(){
int m; //图中的边数
cin>>n>>m;
while(m--){
int x,y;
cin>>x>>y;
g[x].push_back(y);
}
Init_tarjan(); //先初始化
for(int i=1;i<=n;i++){
if(!dfn[i])
Tarjan(i);
}
cout<<answer<<endl;
return 0;
}
相关文章推荐
- 数据结构(java语言描述)树(二叉树)的构建和遍历操作
- unix网络编程笔记(四)--IO复用
- hdu4975 -网络流
- 数据结构之线性表
- 卷积神经网络反向推导算法
- 关于TCP的一些零散知识
- 浏览器 HTTP 协议缓存机制详解
- 安卓使用http下载文件
- ZStack禁止入网
- android中判断网络连接是否可用
- HttpPost实现Request Payload
- 用webmagic实现的网络爬虫
- 数据结构 - Trie树(字典树)
- zz: virsual studio等AP无法访问网络映射驱动器(netmap drive)的解决办法
- HTTP基础与Android之——使用HttpClient和HttpURLConnection
- HTTP 500.22 错误解决
- iOS学习笔记13-网络(二)NSURLSession
- iOS学习笔记14-网络(三)WebView
- iOS学习笔记12-网络(一)NSURLConnection
- 获取网络日期时间,并设置本机系统日期时间