POJ 3177 Redundant Paths

我也慢慢改成了纯c代码的习惯了,原因是前些时间提交做了对比,c和c++的输入输出的差异,导致时间竟能差异150ms以上,呵呵,以前都没注意到

 

题意大致上是: 一群牛将被在一个特定路径构成的农场上迁移,每两块农场之间都至少有一条通道,这些牛要求每两块路径至少要有两条通道,求最少需要修建多少条路才能满足要求

 

思路:每两块农场之间至少有两条通道,也就是说要求添加若干边,将整个图变成一双连通图,需要添加的边的数量和桥的数量有关,图是连通的,因此可以通过查找桥,提取图中的双连通分量,将每一个双连通分量缩成一个点,于是整个连通图,就变成了一个树形图,这时,只需要统计图中度为1的点数量count,因为只要将图中每两个点不重复的添加边,添加(count+1)/2条边就可以了

 

需要注意的是: 题中说明了可能会有平行边,因此在查找桥的时候,用子结点的深度更新当前节点的最远祖先信息要特殊处理,因为这里可能会有多个子结点为父节点

 

#include <stdio.h>
#include <memory.h>

#define init(a) memset(a,0,sizeof(a))
#define MIN(a,b) ((a)<(b)?(a):(b))
#define N 5001
struct Edge
{
 int dest;
 Edge *next;
};
Edge *E[N],mempool[4*N];
int n,m,memh,bridgen,partn;
int root[N],used[N],anc[N],deep[N],part[N],degree[N],bridge[N][2];
void readData();
void addEdge(int u,int v);
void merge(int u,int v);
int findSet(int s);
void dfs(int ,int ,int);
void connex();
int work();

int main()
{
 while (scanf("%d%d",&n,&m)!=EOF)
 {
  readData();
  connex();
  printf("%d/n",work());
 }
 return 0;
}

void connex()
{
 int i,j;
 for (i=1;i<=n;i++)
  root[i]=i;
 init(used);
 bridgen=0;
 for (i=1;i<=n;i++)
  if (!used[i])
  dfs(i,0,1);
 init(part);
 partn=0;
 for (i=1;i<=n;i++)
 {
  j=findSet(i);
  if (!part[j])
  part[j]=++partn;
  part[i]=part[j];
 }
}

int work()
{
 int i,j,k;
 init(degree);
 for (k=1;k<=bridgen;k++)
 {
  i=bridge[k][0];
  j=bridge[k][1];
  degree[part[i]]++;
  degree[part[j]]++;
 }
 int count=0;
 for (i=1;i<=partn;i++)
  if (degree[i]==1)
  count++;
 return (count+1)/2;
}

void dfs(int i,int father,int dth)
{
 anc[i]=deep[i]=dth;
 used[i]=1;
 Edge *L;
 int j,repeat=0;
 for (L=E[i];L!=NULL;L=L->next)
 {
  j=L->dest;
  if (j==father)
  repeat++;
  if ((j!=father||repeat!=1)&&used[j]==1)
  anc[i]=MIN(anc[i],deep[j]);
  if (!used[j])
  {
  dfs(j,i,dth+1);
  anc[i]=MIN(anc[i],anc[j]);
  if (anc[j]<=deep[i])
  merge(i,j);
  else
  {
  bridge[++bridgen][0]=i;
  bridge[bridgen][1]=j;
  }
  }
 }
 used[i]=2;
}

void merge(int i,int j)
{
 int ri=findSet(i);
 int rj=findSet(j);
 if (ri!=rj)
  root[ri]=rj;
}

int findSet(int i)
{
 int j,t;
 for (j=i;root[j]!=j;j=root[j]);
 while (root[i]!=i)
 {
  t=root[i];
  root[i]=j;
  i=t;
 }
 return j;
}

void addEdge(int u,int v)
{
 Edge *L=&mempool[memh++];
 L->dest=v;
 L->next=E[u];
 E[u]=L;
}

void readData()
{
 int i,u,v;
 init(E);
 for (memh=i=0;i<m;i++)
 {
  scanf("%d%d",&u,&v);
  addEdge(u,v);
  addEdge(v,u);
 }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值