题目链接:http://acm.pku.edu.cn/JudgeOnline/problem?id=3177
这个题好假,我一开始用割顶来判断一条边是不是桥,居然wa了,后来就直接判桥brige居然神奇般的AC了。。。不明白。。。
算法:无向图的双连通分量,缩点,求缩点的度。
无向连通分量就是对图进行深搜救可以了,然后记下其中的一些有用信息,比如时间戳,层次度,割顶,桥。具体算法的实现可以参考黑书上(285~)。。。
无向图求割边:以任一节点为起点,深搜,记录每个节点第一次访问的时间DFn,同时记录当前节点不经过其父节点能够访问DFn值最小的节点low。假设a是b的父亲,如果low[b]<=DFn[a],则说明a和b除在同一双连通分量,否则(a,b)是割边,a和b处在不同的双连通分量。
算法:找到割边,获得双连通分量。将同一双连通分量内的节点缩成一个节点,则整个网络变成了一棵树。通过推理可知,把这颗树的叶节点连接起来是最好的方案,需要(cnt%2?(cnt+1)/2:cnt/2)条边。
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; const int MAX=1005; struct Edge { int s,e; }E[10000]; int map[MAX][MAX]; int ancentor[MAX]; int colour[MAX]; int cut[MAX]; int brige[MAX][MAX]; int D[MAX]; int prin[MAX]; int degree[MAX]; int n,m,tol,index,cnt; void init() { memset(degree,0,sizeof(degree)); memset(brige,0,sizeof(brige)); memset(map,0,sizeof(map)); memset(cut,0,sizeof(cut)); memset(colour,0,sizeof(colour)); } void dfs(int node,int father,int deep) { colour[node]=1; D[node]=deep,tol=0; ancentor[node]=D[node]; for(int i=1;i<=n;i++) { if(map[node][i]&&colour[i]==1&&i!=father) ancentor[node]=min(ancentor[node],D[i]); if(map[node][i]&&colour[i]==0) { dfs(i,node,deep+1); tol++; ancentor[node]=min(ancentor[node],ancentor[i]); if((node==1&&tol>1)||(node!=1&&ancentor[i]>=D[node])) cut[node]=1; if(ancentor[i]>D[node]) brige[node][i]=1; } } colour[node]=2; } void Set_colour(int node) { for(int i=1;i<=n;i++) { if(!prin[i]&&map[node][i]) { if(brige[node][i]) prin[i]=index++; else prin[i]=prin[node]; Set_colour(i); } } } int main() { scanf("%d%d",&n,&m); int x,y; init(); for(int i=0;i<m;i++) { scanf("%d%d",&x,&y); E[i].s=x; E[i].e=y; map[x][y]=1; map[y][x]=1; } dfs(1,-1,1); index=2,prin[1]=1; Set_colour(1); for(int i=0;i<m;i++) { int s=prin[E[i].s]; int e=prin[E[i].e]; if(s!=e) { degree[s]++; degree[e]++; } } cnt=0; for(int i=1;i<index;i++) if(degree[i]==1) cnt++; printf("%d/n",(cnt+1)/2); return 0; }