题目链接:http://poj.org/problem?id=1144
描述
一个电话线公司(简称TLC)正在建立一个新的电话线缆网络。他们连接了若干个地点分别从1到N编号。没有两个地点有相同的号码。这些线是双向的并且能使两个地点保持通讯。每个地点的线都终结于电话交换机。每个地点都有一个电话交换机。从每个地点都能通过线缆到达其他任意的地点,然而它并不需要直接连接,它可以通过若干个交换机来到达目的地。有时候某个地点供电出问题时,交换机就会停止工作。TLC的工作人员意识到,除非这个地点是不可达的,否则这种情况就会发生,它还会导致一些其它的地点不能互相通讯。在这种情况下我们会称这个地点(错误发生的地方)为critical。现在工作人员想要写一个程序找到所有critical地点的数量。帮帮他们。
一个电话线公司(简称TLC)正在建立一个新的电话线缆网络。他们连接了若干个地点分别从1到N编号。没有两个地点有相同的号码。这些线是双向的并且能使两个地点保持通讯。每个地点的线都终结于电话交换机。每个地点都有一个电话交换机。从每个地点都能通过线缆到达其他任意的地点,然而它并不需要直接连接,它可以通过若干个交换机来到达目的地。有时候某个地点供电出问题时,交换机就会停止工作。TLC的工作人员意识到,除非这个地点是不可达的,否则这种情况就会发生,它还会导致一些其它的地点不能互相通讯。在这种情况下我们会称这个地点(错误发生的地方)为critical。现在工作人员想要写一个程序找到所有critical地点的数量。帮帮他们。
输入
输入文件包括若组测试数据。每一组是一个网络,每一组测试数据的第一行是地点的总数量N<100. 每个接下来最多N行包括一个数字表示一个地点和与它相连接的地点的数字。这些最多N行完全描述了整个网络,比如,网络中每个直接连接的两个地点被至少一行包括。一行内的所有数字都要用空格隔开。每组数据需要用单独的一个0结束。最后的块只有一行即N=0。
输入文件包括若组测试数据。每一组是一个网络,每一组测试数据的第一行是地点的总数量N<100. 每个接下来最多N行包括一个数字表示一个地点和与它相连接的地点的数字。这些最多N行完全描述了整个网络,比如,网络中每个直接连接的两个地点被至少一行包括。一行内的所有数字都要用空格隔开。每组数据需要用单独的一个0结束。最后的块只有一行即N=0。
输出
输出除了最后一个组其他每一个组的critical地点的数量,每个块用一行输出。
输出除了最后一个组其他每一个组的critical地点的数量,每个块用一行输出。
样例输入:
5
5 1 2 3 4
0
6
2 1 3
5 4 6 2
0
0
5
5 1 2 3 4
0
6
2 1 3
5 4 6 2
0
0
样例输出
1
2
1
2
提示:
你需要确定每行的结束。为了方便判断,每行的结束都没有多余的空白
你需要确定每行的结束。为了方便判断,每行的结束都没有多余的空白
解题思路:求无向图割点的数量
简单介绍下tarjan算法求割点的原理:
观察DFS搜索树,我们可以发现有两类节点可以成为割点:
- 对根节点u,若其有两棵或两棵以上的子树,则该根结点u为割点;
- 对非叶子节点u(非根节点),若其子树的节点均没有指向u的祖先节点的回边,说明删除u之后,根结点与u的子树的节点不再连通,有low[v]>=dfn[u];则节点u为割点。
#include<iostream> #include<vector> #include<cstdio> #include<string> #include<sstream> using namespace std; typedef long long ll; const int maxn=10005; vector<int> mp[maxn]; int n,m,ans,low[maxn],dfn[maxn],par[maxn],ap[maxn],cnt; void init(){ ans=0; cnt=0; for(int i=0;i<=n;i++){ low[i]=dfn[i]=0; par[i]=0; ap[i]=0; mp[i].clear(); } } void tarjan(int u){ dfn[u]=low[u]=++cnt; //cnt记录遍历次序 int son=0; //记录子树数量 for(int i=0;i<mp[u].size();i++){ int v=mp[u][i]; if(!dfn[v]){ //v未被访问,(u,v)为树边 son++; //记录v的父亲节点 par[v]=u; tarjan(v); low[u]=min(low[u],low[v]); //根节点,子树数量大于1即为割点 if(dfn[u]==1&&son>1&&!ap[u]) ap[u]=1,ans++; //其余节点子树可追溯到最早的祖先节点要么为v要么为u else if(dfn[u]!=1&&low[v]>=dfn[u]&&!ap[u]) ap[u]=1,ans++; } else if(par[v]!=u) //节点v已被访问,则(u,v)为回边 low[u]=min(low[u],dfn[v]); } } int main(){ int a,b; string s; while(~scanf("%d",&n)&&n){ init(); getchar(); while(1){ getline(cin,s); stringstream ss(s); ss>>a; if(!a)break; while(ss>>b&&b){ mp[a].push_back(b); mp[b].push_back(a); } } tarjan(1); cout<<ans<<endl; } return 0; }