类型:双连通分量 + 缩点
思路:双连通分量不需要添加新边,将各个双连通分量看做一个点的话,生成树的边即为原图中的桥,此时
题目:http://poj.org/problem?id=3177
来源: USACO 2006 January Gold思路:双连通分量不需要添加新边,将各个双连通分量看做一个点的话,生成树的边即为原图中的桥,此时
添加的边数 = (树中度为1的节点 + 1) / 2
进行Tarjan算法过程中,统计不同桥的信息,如果某边不是桥,则两节点属同一连通分量,进行缩点(并查集实现)然后记录每个节点在新树中的编号,最后通过桥边记录新树各边的度数,统计度数为1的个数
// poj 3177 - Redundant Paths
// ac 256K 0MS
#include <iostream>
#include <string>
#include <cstring>
#include <cstdio>
using namespace std;
#define MIN(a,b) (a < b ? a : b)
#define clr(a,b) memset(a,b,sizeof(a))
#define MAXN 5005
#define MAXM 20010
bool vis[MAXN];
int F, R, bri_num, cnt, num, node;
int d[MAXN];
int tree_node[MAXN]; //存储每个连通分量对应新树的节点
int root[MAXN];
int step[MAXN];
int low[MAXN];
int head[MAXN];
int bridge[MAXN][2]; //存储图中的桥
struct edge {
int v,nxt,id;
}e[MAXM];
void addedge(int u,int v,int id) {
e[cnt].id = id;
e[cnt].v = v;
e[cnt].nxt = head[u];
head[u] = cnt++;
}
void makeset() {
for(int i = 0; i <= F; ++i)
root[i] = i;
}
int findset(int x) {
if(x != root[x])
root[x] = findset(root[x]);
return root[x];
}
void unin(int x,int y) {
root[x] = y;
}
void dfs(int u,int id) {
int i;
for(i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].v;
if(e[i].id != id) {
if(!vis[v]) {
vis[v] = true;
step[v] = low[v] = (++num);
dfs(v, e[i].id);
low[u] = MIN(low[u], low[v]);
}
else
low[u] = MIN(low[u],step[v]);
if(low[v] > step[u]) {
bridge[bri_num][0] = u;
bridge[bri_num++][1] = v;
}
else{
int x = findset(u);
int y = findset(v);
if(x != y)
unin(x,y);
}
}
}
}
int num_cnt() {
int i;
int cnt_num;
dfs(1, -1);
cnt_num = 0;
for(i = 1; i <= F; ++i) {
int x = findset(i);
if(tree_node[x] == -1)
tree_node[x] = cnt_num++;
else
tree_node[i] = tree_node[x];
}
return cnt_num;
}
void init() {
int i, u, v;
clr(head, -1);
clr(vis, false);
clr(tree_node, -1);
clr(d, 0);
makeset();
for(i = 0; i != R; ++i) {
scanf("%d %d", &u, &v);
addedge(u, v, i);
addedge(v, u, i);
}
step[1] = low[1] = num = 1;
cnt = bri_num = node = 0;
vis[1] = true;
}
int main() {
int i, u, v;
while(scanf("%d%d",&F,&R) != EOF) {
init();
int cnt_num = num_cnt();
for(i = 0; i < bri_num; ++i) {
u = bridge[i][0];
v = bridge[i][1];
d[tree_node[u]]++;
d[tree_node[v]]++;
}
for(i = 0; i < cnt_num; ++i)
if(d[i] == 1)
node++;
printf("%d\n", (node + 1) / 2);
}
return 0;
}