Victoria的舞会2——图的连通性及连通分量
【Vijos1022]】Victoria的舞会2
Description
Victoria是一位颇有成就的艺术家,他因油画作品《我爱北京天安门》闻名于世界。现在,他为了报答帮助他的同行们,准备开一个舞会。
Victoria准备邀请n个已经确定的人,可是问题来了:
这n个人每一个人都有一个小花名册,名册里面写着他所愿意交流的人的名字。比如说在A的人名单里写了B,那么表示A愿意与B交流;但是B的名单里不见的有A,也就是说B不见的想与A交流。但是如果A愿意与B交流,B愿意与C交流,那么A一定愿意与C交流。也就是说交流有传递性。
Victoria觉得需要将这n个人分为m组,要求每一组的任何一人都愿意与组内其他人交流。并求出一种方案以确定m的最小值是多少。
注意:自己的名单里面不会有自己的名字。
Input
第一行一个数n。
接下来n行,每i+1行表示编号为i的人的小花名册名单,名单以0结束。1<=n<=200。
Output
一个数,m。
Sample Input
18
0
18 0
0
0
11 0
0
0
0
0
0
5 0
0
0
0
0
0
0
2 0
Sample Output
16
问题分析:
问题描述简要概括为:给出舞会成员之间的关系图(有向图),求解有向图的强连通分量数量。
解法1:
图的连通性+并查集:首先求出任意两点之间的连通性(最短路径),然后将互相连通的顶点使用并查集合并,并统计合并的堆数,即为所求解。
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
/* 本题可以构建一个人与人之间的关系图(有向图),问题转化为求解有向连通图的极大连通子块的数量
求解过程:
第一步,构建有向连通图,并使用Floyd求出各点之间的连通性(顶点数量较少)
第二步,若两点之间互相连通,则把这两个点绑定到一块(分组计数减1),可以使用并查集将两个互相连通的顶点合并
*/
using namespace std;
#define Max 10000
int people[205][205],Link[205][205],father[205]; //people[][]存储每人的花名册,link[][]存储相互之间的连通性
int n,t,temp,ans; int Find(int i)
{
if(i==father[i]) return i;
return Find(father[i]);
} void Merge(int a,int b)
{
a=Find(a);
b=Find(b);
if(a!=b)
{
father[b]=a;
}
} void Floyd()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
{
if(i!=j||i!=k||j!=k)
{
Link[i][j]=min(Link[i][j],Link[i][k]+Link[k][j]);
}
}
} int main()
{
ios::sync_with_stdio(false);
freopen("victoria2.in","r",stdin);
freopen("victoria2.out","w",stdout);
cin>>n;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
{
if(i!=j) Link[i][j]=Max;
}
t=1,ans=n;
while(t<=n)
{
while(cin>>temp)
{
if(temp==0) break;
int seq=++people[t][0];
people[t][seq]=temp;
Link[t][temp]=1;
}
father[t]=t;
++t;
}
Floyd();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i!=j&&Link[i][j]<Max&&Link[j][i]<Max&&Find(i)!=Find(j)) //并查集合并关系网中互相连通的顶点
{
ans--;
Merge(i,j);
}
}
cout<<ans<<endl;
return 0;
}
解法2:
采用tarjan求有向图的强连通分量,主要使用栈的结构及访问时间戳,Low[ ]数组标记
#include <cstdio>
#include <stack>
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std; vector<int> point[205];
stack<int> S; //栈S用于存储tarja深度搜索过程中顶点的拓扑序列
//InStack[]标记顶点是否在栈中,DFS[]标记顶点的访问时间戳,Low(u)为u或u的子树能追溯到最早的栈中节点次序号
//Belong[]标记顶点属于哪一个强连通分量,index访问时间戳,nun强连通分量计数
int InStack[205]={0},DFS[205],Low[205],Belong[205],Index=0,num=0; void tarjan(int u)
{
DFS[u]=Low[u]=++Index;
S.push(u);
InStack[u]=1;
for(vector<int>::iterator v=point[u].begin();v!=point[u].end();++v)
{
if(!DFS[*v])
{
tarjan(*v);
Low[u]=min(Low[u],Low[*v]);
}
else if(InStack[*v])
{
Low[u]=min(Low[u],DFS[*v]);
}
}
if(DFS[u]==Low[u])
{
int e;
++num;
while(!S.empty())
{
e=S.top();
S.pop();
InStack[e]=0;
Belong[e]=num;
if(u==e) break;
}
}
} int main()
{
// ios::sync_with_stdio(false); //注意 cin scanf 与cout printf 在关闭同步输入输出流的时候,混合使用会出错
freopen("victoria2.in","r",stdin);
// freopen("victoria2.out","w",stdout);
int n,t,temp;
// scanf("%d",&n);
cin>>n; //这个地方用了cin流读入
t=1;
while(t<=n)
{
while(scanf("%d",&temp)) //这里使用标准scanf()读入,同时存在cin,scanf不能关闭输入输出流同步
{
if(!temp) break;
point[t].push_back(temp);
}
++t;
}
for(int i=1;i<=n;++i)
{
if(!DFS[i])
{
tarjan(i);
}
}
// printf("%d\n",num);
cout<<num<<endl;
return 0;
}
解法3:
求强连通分量算法Kosaraju,正反图双向搜索,寻找解答树
#include<cstdio>
#include<stack>
#include<cstring>
/*
Kosaraju算法求连通分量,采用正反图双向DFS求连通分量。
根据:有向图的这样一个性质,一个图和他的transpose graph(边全部反向)具有相同的强连通分量。
算法求解步骤:
1.对G求解Reverse Post-Order,即得到顶点的"伪拓扑排序"
2.对G进行转置得到GR
3.按照第一步得到的集合中顶点出现的顺序,对GR调用DFS得到若干颗搜索树
4.每一颗搜索树就代表了一个强连通分量
这个算法的想法很巧妙,为了突出回向边,对图进行转置,然后对转置的图按照之前得到的顶点(拓扑)序列进行DFS调用。
*/
//Map[][]原图关系,rMap[][]反图关系,pcolor[]顶点染色信息,Count连通分量的计数
int Map[205][205],rMap[205][205],pcolor[205]={0},Count=0,N;
std::stack<int> S; void dfs(int u) //正向染色,得到拓扑序列顶点集,存放在栈S
{
pcolor[u]=1;
S.push(u);
for(int v=1;v<=N;++v)
{
if(Map[u][v]==1&&!pcolor[v])
dfs(v);
}
} void rdfs(int u) //反向调用,寻找解的搜索树
{
pcolor[u]=Count; //标记u属于连通分量的标号
for(int v=1;v<=N;++v)
{
if(rMap[u][v]==1&&!pcolor[v])
rdfs(v);
}
} int main()
{
freopen("victoria2.in","r",stdin);
freopen("victoria2.out","w",stdout);
scanf("%d",&N);
int t=1;
while(t<=N)
{
int temp;
while(scanf("%d",&temp))
{
if(temp==0) break;
Map[t][temp]=1;
rMap[temp][t]=1;
}
++t;
}
for(int i=1;i<=N;++i)
{
if(!pcolor[i]) dfs(i);
}
/*
while(!S.empty())
{
printf("%d ",S.top());
S.pop();
}
printf("\n");
*/
memset(pcolor,0,sizeof(pcolor));
while(!S.empty()) //根据正向生成顶点拓扑序列集,进行反向DFS调用,寻找解答搜索树
{
int x=S.top();
S.pop();
if(!pcolor[x])
{
++Count;
rdfs(x);
}
}
printf("%d\n",Count);
return 0;
}
Victoria的舞会2——图的连通性及连通分量的更多相关文章
- 数据结构-图-Java实现:有向图 图存储(邻接矩阵),最小生成树,广度深度遍历,图的连通性,最短路径1
import java.util.ArrayList; import java.util.List; // 模块E public class AdjMatrixGraph<E> { pro ...
- [vijos P1023] Victoria的舞会3
这… 本来想学习一下Tarjan算法的,没想到码都码好了发现这题不是求强连通分量而是简单的连通分量…图论基础都还给老师了啊啊啊!最后深搜通通解决! v标记是否被访问过,scc标记每个的祖先(本来想写T ...
- POJ 2513 - Colored Sticks - [欧拉路][图的连通性][字典树]
题目链接: http://poj.org/problem?id=2513 http://bailian.openjudge.cn/practice/2513?lang=en_US Time Limit ...
- poj 3310(并查集判环,图的连通性,树上最长直径路径标记)
题目链接:http://poj.org/problem?id=3310 思路:首先是判断图的连通性,以及是否有环存在,这里我们可以用并查集判断,然后就是找2次dfs找树上最长直径了,并且对树上最长直径 ...
- POJ2513(字典树+图的连通性判断)
//用map映射TLE,字典树就AC了#include"cstdio" #include"set" using namespace std; ; ;//26个小 ...
- 图的连通性问题的小结 (双连通、2-SAT)
图的连通性问题包括: 1.强连通分量. 2.最小点基和最小权点基. 3.双连通. 4.全局最小割. 5.2-SAT 一.强连通分量 强连通分量很少单独出题,一般都是把求强连通分量作为缩点工具. 有三种 ...
- 2018年牛客多校寒假 第四场 F (call to your teacher) (图的连通性)
题目链接 传送门:https://ac.nowcoder.com/acm/contest/76/F 思路: 题目的意思就是判断图的连通性可以用可达性矩阵来求,至于图的存储可以用邻接矩阵来储存,求出来可 ...
- POJ3177 Redundant Paths 图的边双连通分量
题目大意:问一个图至少加多少边能使该图的边双连通分量成为它本身. 图的边双连通分量为极大的不存在割边的子图.图的边双连通分量之间由割边连接.求法如下: 求出图的割边 在每个边双连通分量内Dfs,标记每 ...
- 算法练习_图的连通性问题(JAVA)
一.问题 1.问题描述: 有n个点(1...n),输入整数对(8,9),表示8,9点之间存在相互的连接关系. 动态连通性问题--编写一段程序过滤掉所以无意义的整数对,即为在不破坏图连通性的前提下,以最 ...
随机推荐
- shell 脚本加密
日常编写shell脚本时会写一些账号和密码写入脚本内,但是不希望泄露账号密码,所以对shell脚本进行加密变成可执行文件. 主要使用 shc 对 Linux shell 脚本加密,shc是一个专业的加 ...
- H: Dave的组合数组(二分法)
Dave的组合数组 Time Limit: C/C++ 1 s Java/Python 3 s Memory Limit: 128 MB Accepted: 3 ...
- python no module named builtins
使用python2时出现此错误. 解决办法: pip install future
- 下载离线VS2017
1.下载工具 版本 文件 Visual Studio Enterprise (企业版) vs_enterprise.exe Visual Studio Professional (专业版) vs_pr ...
- fatal error c1001 编译器中发生内部错误 OpenMesh6.3
Internal Compiler Error VS 2015 Update1 VS2015 Update1 编译OpenMesh的额代码时发生错误 fatal error c1001 编译器中发生内 ...
- Python_logging模块
日志:方便用户了解系统.软件或应用的运行情况,及时发现问题并快速定位.解决问题. 一个日志信息对应的是一个事件的发生,而一个事件需要包括的几个内容: 事件发生时间 事件发生位置 事件发生严重程度(日志 ...
- Python_lambda简单函数表达式
lambda表达式只能用于简单函数的书写 def funx(a): a+=1 return a print(funx(99)) 用lambda实现上面函数: funx = lambda a: a+1 ...
- Kafka/Zookeeper集群的实现(二)
[root@kafkazk1 ~]# wget http://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.4.12/zookeeper-3.4.12. ...
- Codeforces 873E Awards For Contestants ST表
原文链接https://www.cnblogs.com/zhouzhendong/p/9255885.html 题目传送门 - CF873E 题意 现在要给 $n(n\leq 3000)$ 个学生颁奖 ...
- BZOJ1001 [BeiJing2006]狼抓兔子 最小割 对偶图 最短路
原文链接http://www.cnblogs.com/zhouzhendong/p/8686871.html 题目传送门 - BZOJ1001 题意 长成上面那样的网格图求最小割. $n,m\leq ...