POJ 3177 / POJ 3352 : Redundant Paths / Road Construction - 边双连通分量,缩点

来源:互联网 发布:淘宝买家秀福利的店铺 编辑:程序博客网 时间:2024/05/16 12:59
题意:
给定现有的R条直接连接2个牧场的路,F-1<=R<=10,000,计算至少需要新修多少条直接连接2个牧场的道路,使得任何2个牧场之间至少有2条独立的路。
分析:
见代码及注释……
《图论算法理论实现应用》——P412
3177Accepted700K16MSG++2320B2014-03-14 16:55:30

(啊,今天是过3177后的第三天晚上==!)
接着做练习题发现3177和3352是一模一样的……只是数据范围不同罢了。
 3352Accepted760K16MSG++2320B2014-03-16 20:18:33

/* POJ 3177; *边双连通分量里所有点可等价地看作一个点进行缩点——缩点后解决“在新图的树中至少添加多少条边能使图变为边双连通图”——添加边数=(书中度为1的结点数+1)/2. *采用并查集缩点,缩点后统计得到的树中叶子结点的个数,然后求应添加的边数 */#include<iostream>#include<cstdio>#include<cstring>#define clr(a) memset(a,0,sizeof(a))using namespace std;const int N=1005,M=20005;int n,m,w;//顶点数;边数;原图边双连通分量个数int belong[N],low[N],dfn[N],visited[N];//;顶点i可达祖先的最小编号;深度优先数;0未访问,1已访问,2已访问且已检查邻接顶点int bridge[M][2],nbridge;struct Node{int j;//另一顶点序号Node *next;//下一个边结点};Node mem[M];int memp;//储存边结点的数组;数组中的序号Node *e[N];//邻接表int MIN(int a,int b){return a>b?b:a;}/*邻接表中插入边(i,j)*/void addEdge(Node *e[],int i,int j){Node *p=&mem[memp++];p->j=j;p->next=e[i];e[i]=p;}int FindSet(int f[],int i){int j=i,t;while(f[j]!=j)j=f[j];while(f[i]!=i){t=f[i];f[i]=j;i=t;}return j;//最后进行缩点的点}void UniteSet(int f[],int i,int j){int p=FindSet(f,i),q=FindSet(f,j);if(p!=q)f[p]=q;}/*搜索双连通分量——顶点,?,深度,并查集数组*/void dfs_2conn(int i,int father,int dth,int f[]){int j,tofather=0;Node *p;visited[i]=1;low[i]=dfn[i]=dth;for(p=e[i];p!=NULL;p=p->next){j=p->j;if(visited[j]==1 && (j!=father||tofather))low[i]=MIN(low[i],dfn[j]);if(visited[j]==0){dfs_2conn(j,i,dth+1,f);low[i]=MIN(low[i],low[j]);if(low[j]<=dfn[i])//i,j在同一个双连通分量 则利用UnionSet缩点UniteSet(f,i,j);if(low[j]>dfn[i]){//(i,j)是桥bridge[nbridge][0]=i;bridge[nbridge++][1]=j;}}if(j==father)tofather=1;}visited[i]=2;}/*求无向图极大边双连通分量的个数*/int DoubleConn(){int i,k,f[N],ncon=0;for(i=0;i<n;i++){f[i]=i;//并查集数组belong[i]=-1;}clr(visited);nbridge=0;dfs_2conn(0,-1,1,f);for(i=0;i<n;i++){k=FindSet(f,i);if(belong[k]==-1)belong[k]=ncon++;belong[i]=belong[k];}return ncon;}int main(){int i,j,k;while(~scanf("%d%d",&n,&m)){memp=0;clr(e);for(k=0;k<m;k++){scanf("%d%d",&i,&j);i--;j--;addEdge(e,i,j);addEdge(e,j,i);//相互地,故(i,j),(j,i)都插入链表}w=DoubleConn();int d[N]={0};//收缩后各顶点度数for(k=0;k<nbridge;k++){i=bridge[k][0];j=bridge[k][1];d[belong[i]]++;d[belong[j]]++;}int cnt=0;//缩点以后 叶子结点个数for(i=0;i<w;i++)if(d[i]==1)cnt++;printf("%d\n",(cnt+1)/2);}return 0;}


0 0
原创粉丝点击