Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 8558 | Accepted: 3712 |
Description
Input
Output
Sample Input
7 7 1 2 2 3 3 4 2 5 4 5 5 6 5 7
Sample Output
2
Hint
1 2 3 +---+---+ | | | | 6 +---+---+ 4 / 5 / / 7 +Building new paths from 1 to 6 and from 4 to 7 satisfies the conditions.
1 2 3 +---+---+ : | | : | | 6 +---+---+ 4 / 5 : / : / : 7 + - - - -Check some of the routes:
题目大意:有F个牧场,1<=F<=5000,现在一个牧群经常需要从一个牧场迁移到另一个牧场。奶牛们已经厌烦老是走同一条路,所以有必要再新修几条路,这样它们从一个牧场迁移到另一个牧场时总是可以选择至少两条独立的路。现在F个牧场的任何两个牧场之间已经至少有一条路了,奶牛们需要至少有两条。
给定现有的R条直接连接两个牧场的路,F-1<=R<=10000,计算至少需要新修多少条直接连接两个牧场的路,使得任何两个牧场之间至少有两条独立的路。两条独立的路是指没有公共边的路,但可以经过同一个中间顶点
解题思路:给定一个无向连通图,判断至少需要加多少条边,使得任意两点之间至少有两条相互‘边独立’的道路,也就是说,至少加多少条边,使得这个图成为一个边双连通图。
首先我们已经有了一个连通图
那么我们将所有的双连通块看成一个点,这样可以得到一颗树,因为他们当中没有环,而又是一个连通的图,所以我们可以肯定的是,这样缩点之后可以得到一颗树,这样的树至少需要加多少条边就能构成一个双连通图呢,我们只需要将叶子节点连起来即可,所以我们最少要连的边=(叶子节点数+1)/2
利用low的性质在一个连通块中的low值是相同的
而求边连通分量,用tarjan就可以求出哪个点属于哪个双连通块中,然后再计算度为1的节点就可以了
程序:
#include"stdio.h" #include"string.h" #include"iostream" #define M 10009 #define inf 99999999 using namespace std; struct st { int u,v,w,next; }edge[50009]; int head[M],dfn[M],low[M],use[M],bridge[M],num,t,indx,suo[M],used[M]; int min(int a,int b) { return a<b?a:b; } void init() { t=0; memset(head,-1,sizeof(head)); } void add(int u,int v) { edge[t].u=u; edge[t].v=v; edge[t].next=head[u]; head[u]=t++; } void tarjan(int u,int id) { dfn[u]=low[u]=++indx; int i; for(i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].v; if(i==(id^1))continue; if(!dfn[v]) { tarjan(v,i); low[u]=min(low[u],low[v]); if(low[v]>dfn[u]) { bridge[num++]=i; } } else low[u]=min(low[u],dfn[v]); } } void solve(int n) { num=indx=0; memset(dfn,0,sizeof(dfn)); for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,-1); } int main() { int n,m; while(scanf("%d%d",&n,&m)!=-1) { init(); while(m--) { int a,b; scanf("%d%d",&a,&b); int flag=0; for(int i=head[a];i!=-1;i=edge[i].next)//去重边 { int v=edge[i].v; if(b==v) flag++; } if(flag) continue; add(a,b); add(b,a); } solve(n); memset(use,0,sizeof(use)); int k=0; memset(suo,0,sizeof(suo)); memset(used,0,sizeof(used)); for(int i=1;i<=n;i++)//把原图进行缩点,形成一颗树节点为k个 { if(!used[low[i]]) { used[low[i]]++; suo[k++]=low[i]; } } for(int i=0;i<t;i+=2)//求度数 { int u=edge[i].u; int v=edge[i].v; if(low[u]!=low[v]) { use[low[u]]++; use[low[v]]++; } } int ans=0; for(int i=0;i<k;i++) { if(use[suo[i]]==1)//统计入度为一的点的个数 ans++; } printf("%d\n",(ans+1)/2); } return 0; }