hdu4612:http://acm.hdu.edu.cn/showproblem.php?pid=4612
题意:给你一个无向连通图,问加上一条边后得到的图的最少的割边数;
题解:首先对原图求割边数,然后缩点之后建树,然后求树的直径。因为加上一条边,能消耗最大的割边就是树的直径。一道很好的模板题目。
#pragma comment(linker,"/STACK:100000000,100000000")//阔栈的语句
#include <stdio.h>
#include <string.h>
#include <vector>
#define pb push_back
using namespace std;
const int maxn = ;
const int maxm = ;
struct EDGE{
int next, to, vis;
}edge[maxm*];//记录边,vis表示这一条边是否被访问
struct GEEDGE {
int u, to;
}geedge[maxm];//记录割边
int n,m,time,top,type,getot,cnt;//time是时间戳,type标记连通块,getot割边的数量,cnt边数
int head[maxn],st[maxn],dfn[maxn],low[maxn],belo[maxn];//belo[i],表示i属于哪个连通块
int start,ans1,ans,u,v;
void init(){
time=top=type=getot=cnt=;
memset(head,-,sizeof(head));
memset(dfn,,sizeof(dfn));
memset(low,,sizeof(low));
memset(belo,,sizeof(belo));
}
void add(int u,int v){
edge[cnt].to=v;
edge[cnt].next=head[u];
edge[cnt].vis=;
head[u]=cnt++;
}
void dfs(int u) {
low[u] = dfn[u] = ++time;
st[++top] = u;
for(int i = head[u];i != -;i = edge[i].next) {
if(edge[i].vis) continue;
edge[i].vis = edge[i^].vis = ;
int to = edge[i].to;
if(!dfn[to]) {
dfs(to);
low[u] = min(low[u], low[to]);
if(low[to] > dfn[u]) {//表示找到一个连通块
type++;
int v;
do {
v = st[top--];
belo[v] = type;//表示v属于第type个连通块
} while(v != to);
geedge[++getot].u = u;//记录割边的起点
geedge[getot].to = to;//记录割边的另一个点
}
}
else
low[u] = min(low[u], low[to]);
}
}
void DFS(int len,int fa,int now){//求树的直径
if(len>ans){
ans=len;
start=now;
}
for(int i=head[now];i!=-;i=edge[i].next){
int v=edge[i].to;
if(v!=fa) DFS(len+,now,v);
}
}
int main(){
while(~scanf("%d%d",&n,&m)&&n){
init();
for(int i=;i<=m;i++){
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
for(int i=;i<=n;i++)
if(!dfn[i])
dfs(i);
ans1=getot;
memset(head,-,sizeof(head));cnt=;//刷新边,接下来从新加边
for(int i=;i<=ans1;i++){
add(belo[geedge[i].to],belo[geedge[i].u]);
add(belo[geedge[i].u],belo[geedge[i].to]);
}
ans = ;
DFS(,-,);//
ans=;
DFS(,-,start);//两遍DFS求树的直径
printf("%d\n", getot-ans);
}
}