比赛
题目
题意:给出n个点和m条边的图,存在重边,问加一条边以后,剩下的桥的数量最少为多少。
解法:先用tarjan缩点变成一棵树,然后求树的直径,显然,用一条边将直径的两个端点连起来可以让剩余的桥数量最少。要注意会有重边,在tarjan里面要判一下。
//time 1031MS //memory 31340K #pragma comment(linker, "/STACK:1024000000,1024000000") #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #define MAXN 300015 #define MAXM 4000015 using namespace std; struct Edge{ int v,next; }edge[MAXM],edge2[MAXM]; int head[MAXN],en; int head2[MAXN],en2; int id[MAXN],dfn[MAXN],low[MAXN],sta[MAXN],top,num,scc; int n,m; bool vis[MAXN]; void init() { memset(head,-1,sizeof(head)); memset(vis,0,sizeof(vis)); en = 0; top = 0; scc=num = 0;memset(dfn,0,sizeof(dfn)); } void addedge(int u,int v) { edge[en].v = v; edge[en].next = head[u]; head[u] = en++; } void addedge2(int u,int v) { edge2[en2].v = v; edge2[en2].next = head2[u]; head2[u] = en2++; } void tarjan(int u,int fa) { dfn[u] = low[u] = ++num; sta[++top] = u; int cnt=0; for(int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].v; if(!dfn[v]) { tarjan(v,u); low[u] = min(low[u],low[v]); } else if (fa==v) { if (cnt) low[u] = min(low[u],dfn[v]);//重边 cnt++; } else low[u] = min(low[u],dfn[v]); } if(dfn[u]==low[u]) { int x; scc++; do { x = sta[top--]; id[x] = scc; }while(x!=u); } } void build() { en2 = 0; memset(head2,-1,sizeof(head2)); for(int i = 1; i <= n; i++) { for(int j = head[i]; j!=-1; j = edge[j].next) { int v = edge[j].v; if(id[i]!=id[v]) addedge2(id[i],id[v]); } } } int ans; int dfs(int u,int p) { int max1=0,max2=0; for (int i=head2[u];i!=-1;i=edge2[i].next) { int v=edge2[i].v; if (v==p) continue; int tmp=dfs(v,u)+1; if (max1<tmp) max2=max1,max1=tmp; else if (max2<tmp) max2=tmp; } ans=max(ans,max1+max2); return max1; } int main() { //freopen("/home/qitaishui/code/in.txt","r",stdin); int u,v; while(scanf("%d%d",&n,&m)&&(n+m)) { init(); //cout<<n<<m<<endl; for(int i = 0; i < m; i++) { scanf("%d%d",&u,&v); if (v==u) continue; addedge(u,v); addedge(v,u); //cout<<u<<" "<<v<<endl; } tarjan(1,-1); build(); ans=0; dfs(1,-1); printf("%d\n",scc-ans-1); } return 0; }