题目大意:一个队想遍访所有小城,每个队每条路只能走一次,该队可被分成了很多小队,求得使能够遍访所有小城的最小队数。
算法:欧拉路+并查集
难度:NOIP
题解:
首先,用并查集处理出每个连通块,并且读入时统计每个点的度数,然后在每个连通块内进行枚举,如果一个连通块内都是偶数度的点,那么这个连通块就可以由一个小队走完,如果这个连通块内有奇数度的点,那么就说明一个小队是走不完的,该集合构成了非欧拉回路的其他连通集。则需要奇度总数/2个小队数。因为一条欧拉路径最多有两个奇度点
注意:孤立的一个点不用去考虑!
代码如下(my code) 有点乱:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <queue>
#define ll long long
#define N 100005
using namespace std;
int fa[N];
int findf(int x)
{
if(x==fa[x]) return x;
return fa[x]=findf(fa[x]);
}
int deg[N];
int vis[N];
struct node
{
int idd;
int tag;
}aa[N];
int cmp(node x,node y)
{
return x.tag< y.tag;
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(deg,0,sizeof(deg));
memset(vis,0,sizeof(vis));
memset(aa,0,sizeof(aa));//这个数组一定要记得清!!!
for(int i = 1;i <= n;i++)
{
fa[i]=i;
}
for(int i = 1;i <= m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
deg[a]++,deg[b]++;
int t1=findf(a);
int t2=findf(b);
if(t1!=t2) fa[t1]=t2;
vis[a]=1,vis[b]=1;
}
for(int i = 1;i <= n;i++)
{
if(!vis[i]) continue;
int f=findf(i);
aa[i].tag=f;
aa[i].idd=i;
}
sort(aa+1,aa+1+n,cmp);
int ans=0;
int aas=0,ctt=0;
for(int i = 1;i < n;i++)
{
if(aa[i].tag==0) continue;
if(aa[i].tag==aa[i+1].tag)
{
aas+=deg[aa[i].idd]+deg[aa[i+1].idd];
if(deg[aa[i].idd]%2) ctt++;
if((deg[aa[i+1].idd]%2)&&(i==n-1)) ctt++;
}else
{
if(ctt)
{
ans+=(ctt+1)/2;
}else
{
ans+=1;
}
ctt=0;
aas=0;
}
if(i==n-1)
{
if(ctt)
{
ans+=(ctt+1)/2;
}else
{
ans+=1;
}
ctt=0;
aas=0;
}
}
printf("%d\n",ans);
}
return 0 ;
}
大佬代码如下:
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=1e5+5;
int par[maxn],in[maxn],cnt[maxn],odd[maxn];
int N,M;
int a,b;
int ans;
void init()
{
for(int i=1;i<=N;i++)
par[i]=i;
memset(in,0,sizeof(in));
memset(cnt,0,sizeof(cnt));
memset(odd,0,sizeof(odd));
ans=0;
}
int find(int a)
{
if(a==par[a])return a;
return par[a]=find(par[a]);
}
void unite(int a,int b)
{
a=find(a);
b=find(b);
if(a==b)return ;
else if(a>b)
par[a]=b;
else
par[b]=a;
}
int main()
{
ios::sync_with_stdio(false);
while(cin>>N>>M)
{
init();
for(int i=0;i<M;i++)
{
cin>>a>>b;
unite(a,b);
in[a]++;in[b]++;//统计节点度
}
for(int i=1;i<=N;i++)
{
cnt[find(i)]++;//统计i的根节点的节点数
if(in[i]%2)odd[find(i)]++;//统计i的根节点的节点奇度数
}
for(int i=1;i<=N;i++)
{
if(cnt[i]<=1)continue;//独立的点不用考虑或者不为根节点的点不考虑
else if(odd[i]==0)ans++;//构成欧拉回路一笔画
else if(odd[i]>0)ans+=odd[i]/2;//没有构成欧拉回路奇度/2
}
cout<<ans<<endl;
}
return 0;
}