题目链接:http://poj.org/problem?id=1523
解析:这题就是求无向图的一个割点,用tarjan算法,其他的倒是没什么难的,算是割点的一个入门题吧。
tarjan算法入门的话,建议看看这个博客http://blog.csdn.net/lyy289065406/article/details/6752662,讲的很详细,但是代码就完全看不懂了(好吧,没学c++╮(╯▽╰)╭)
不过tarjan算法还是挺难理解的,如果不会的话,大家可以自行模拟一下,而且算法中用到的数组的各自的含义我在注释中也说了
#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
#include<queue>
#include<functional>
#define MEM(x,y) memset(x,y,sizeof(x))
#define INF 1010
using namespace std;
int low[INF]; //low[i]用来存节点i及其子树中的点通过非父子边(或树边)所能到达的最早的祖先的层数
int deep[INF]; //deep[i]存节点i自身第一次被遍历所在的层数
int sum[INF]; //sum[i]是存如果i是割点,则其删除它有几个连通分支
vector<int>graph[INF];
priority_queue<int,vector<int>,greater<int> >ans; //ans用来存割点,因为需要由小到大输出
int dfs_deep; //时间戳,我习惯记为层数
void tarjan(int node,int prenode) //node是当前递归的节点,prenode是其父节点
{
low[node] = deep[node] = ++dfs_deep;
int len = graph[node].size();
int son = 0; //记录该节点的分支的个数
for(int i = 0 ; i < len ; i ++)
{
if(!deep[graph[node][i]])
{
tarjan(graph[node][i],node);
low[node] = min(low[node],low[graph[node][i]]); //更新node和其子节点所能到达的最早的祖先层数
if(low[graph[node][i]] >= deep[node]) //node满足作为割点的条件
sum[node]++;
son++;
}
else
if(graph[node][i] != prenode) //不是其直系父节点,即此边不是dfs树中的边
low[node] = min(low[node],deep[graph[node][i]]);//举个例子,由2开始,2--4--5,其中2--5也有边,那么当
//递归到5是,2作为5的子节点,虽然已经遍历过了,但是low[5]仍得更新为deep[2],
//注意此式low[node] = min(low[node],low[graph[node][i]])是错的
}
if(prenode == 0)
{
if(son>= 2) //如果node是根节点且分支数量大于等于2,则一定是割点
{
sum[node] = son;
ans.push(node);
}
}
else if(sum[node] > 0) //如果该点是割点,此时sum[node]存的是该点的分支数减1,因为sum[]初值为0
{
sum[node]++;
ans.push(node);
}
}
int main()
{
int a,b,maxnode; //maxnode存共有几个节点
int Case = 0;
while(1)
{
maxnode = -1;
MEM(low,0);
MEM(deep,0);
MEM(sum,0);
scanf("%d",&a);
if(a == 0)
break;;
for(int i = 0 ; i < INF; i ++)
graph[i].clear();
while(!ans.empty())
ans.pop();
while(1)
{
maxnode = max(maxnode,a);
scanf("%d",&b);
maxnode = max(maxnode,b);
graph[a].push_back(b);
graph[b].push_back(a);
scanf("%d",&a);
if(!a)
break;
}
dfs_deep = 0;
for(int i = 1; i <= maxnode; i ++)
if(!deep[i] && graph[i].size() > 0) //该点没有被递归过,肯定是一个新的连通分支
tarjan(i,0); //递归每个连通分支
int len = ans.size();
printf("Network #%d\n",++Case);
if(len == 0)
printf(" No SPF nodes\n");
for(int i = 0 ; i < len ; i ++)
{
int temp = ans.top();ans.pop();
printf(" SPF node %d leaves %d subnets\n",temp,sum[temp]);
}
printf("\n");
}
return 0;
}