题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3018
题目大意:n个蚂蚁村庄之间有m条道路,一群蚂蚁想要遍历所有的村庄,且每条道路只走一次,问至少需要把这群蚂蚁分成多少组才能实现目标。
分析:我们假设n个村庄之间有k个连通分量,对于每一个连通分量,如果存在欧拉路径,那么该分量需要一组蚂蚁就行;如果不存在欧拉路径,我们知道,要想遍历该分量所有的顶点且每条边只走一次,如果所有的顶点都有偶数个度数,那么就存在欧拉回路了,也只需要一组蚂蚁,如果存在奇数度数的顶点,而且又要想一笔画走完所有边,我们就要从这个顶点开始出发,到另一个奇数度数的顶点为止==>即两个奇数度数的顶点确定一条路径,但该路径并不一定覆盖该连通分量重所有的顶点,对于其他的顶点,我们仍然遵循上面的步骤,这样对于每一个不存在欧拉路径的连通分量,他需要的步数就等于该连通分量中所有奇数度顶点的个数除以2.分别把这些不存在欧拉路径的连通分量的步数加起来,然后再加上存在欧拉路径的步数,就是最终的结果了。
实现代码如下:
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=100005;
int ind[maxn];
bool vis[maxn];
int par[maxn],ran[maxn];
void init(int n)
{
for(int i=1;i<=n;i++)
{
par[i]=i;
ran[i]=1;
}
}
int Find(int x)
{
if(par[x]!=x)
return par[x]=Find(par[x]);
return x;
}
void Union(int x,int y)
{
x=Find(x);
y=Find(y);
if(x==y) return ;
if(ran[x]>ran[y])
{
par[y]=x;
ran[x]+=ran[y];
}
else
{
par[x]=y;
ran[y]+=ran[x];
}
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=-1)
{
init(n);
int u,v;
memset(ind,0,sizeof(ind));
memset(vis,0,sizeof(vis));
for(int i=0;i<m;i++)
{
scanf("%d%d",&u,&v);
Union(u,v);
ind[u]++;
ind[v]++;
}
for(int i=1;i<=n;i++)
if(par[i]==i) vis[i]=1;
int odd=0,ans=0;
for(int i=1;i<=n;i++)
{
if(ind[i]%2)
{
int x=Find(i);
if(vis[x]) vis[x]=0;
odd++;
}
}
for(int i=1;i<=n;i++)
if(vis[i]&&ind[i]) ans++;
printf("%d\n",ans+odd/2);
}
return 0;
}