Problem Decription
很裸的求割点 个数的题目。第一行给你n,代表该图有n个点。接下来每行给你一个u,u == 0退出循环,给你很多v,代表u到v有一条无向边。
思路:num[]数组记录该点第几个走到(num[]值是不变的),low[]值是不断更新的,最开始等于num[]值,找到祖先了也就是更小的num[]的时候就更新。回溯的时候和孩子的low[]值比较更新,因为孩子能到的地方,父亲也能到,如果不能回到祖先,就代表有割点也就是low[v] >= num[u],u就是割点。不连通的孩子个数大于一个,也代表父亲是割点
#include<bits/stdc++.h>
using namespace std;
#define mm 105
struct node
{
int to, next;
};
node Map[mm * mm];
int low[mm], num[mm];//low改变,num不变,都是记录时间戳
int head[mm], flag[mm];//flag[]用来标记那个点是割点
int n, root, sig;
void add(int u, int v, int &cnt)//前向星存图
{
Map[cnt].to = v;
Map[cnt].next = head[u];
head[u] = cnt++;
}
void dfs(int u, int father)
{
int son = 0;
low[u] = num[u] = sig++;//一开始low == num
for(int i = head[u]; ~i; i = Map[i].next)
{
int to = Map[i].to;
if(!num[to])
{
son++;
dfs(to, u);
low[u] = min(low[to], low[u]);//回溯的时候,更新low[u]的值,因为孩子能到的,父亲都能到
if(u != root && low[to] >= num[u]){//回不到祖先,代表u是割点
flag[u] = 1;
}
if(u == root && son > 1)//不连通孩子数大于1
{
flag[u] = 1;
}
}
else if(to != father)//更新low[]值
{
low[u] = min(low[u], num[to]);
}
}
return ;
}
int main()
{
char c;
int cnt, u, v;
while(~scanf("%d", &n) && n)
{
cnt = 0;//初始化
memset(head, -1, sizeof(head));
memset(num, 0, sizeof(num));
memset(low, 0, sizeof(low));
memset(flag, 0, sizeof(flag));
while(~scanf("%d", &u) && u)//存图
{
while(~scanf("%d%c", &v, &c))
{
add(u, v, cnt);
add(v, u, cnt);
if(c == '\n') break;
}
}
sig = 1;//初始化
root = 1;
dfs(1, root);
int ans = 0;
for(int i = 1; i <= n; i++)//割点数
{
if(flag[i]) ans++;
}
printf("%d\n", ans);
}
return 0;
}