描述:
给定n点m边无向图,可能有自环和重边。 问最少添加多少条边后,使得图存在从点1出发又回到点1的欧拉回路(所给的边要保证经过一次)。 n,m ≤ 106
思路:
利用欧拉回路存在的性质,先求出每个连通块内度数是奇数的点的个数。 我们需要加边 以消除所有奇度数点。 然后我们还得把所有连通块连通起来。 可以发现,如果一个连通块包含奇数度数点,那 么就不需要额外加边就能与其他连通块连通 (想象把这些连通块的奇数点收尾相连)。 但如果一个连通块里没有奇度数点,那么必须额外花费1条边才能把它与其他部分连接起 来。 于是答案是:
• 如果全图连通,则答案是奇数度数点的个数/2.
• 否则答案是奇数度数点的个数/2+不含奇数度数点的连通块的个数。
注意:
有一些细节要考虑。 注意孤立点的特殊处理(有没有自环,有的话需要当做联通分量连入图中,没有的话就还是孤立点)。
代码:
#include <bits/stdc++.h>
#define pr(x) cout << #x << "= " << x << " " ;
#define pl(x) cout << #x << "= " << x << endl;
#define ll __int64
using namespace std;
template<class T> void read(T&num) {
char CH; bool F=false;
for(CH=getchar();CH<'0'||CH>'9';F= CH=='-',CH=getchar());
for(num=0;CH>='0'&&CH<='9';num=num*10+CH-'0',CH=getchar());
F && (num=-num);
}
int stk[70], tp;
template<class T> inline void print(T p) {
if(!p) { puts("0"); return; }
while(p) stk[++ tp] = p%10, p/=10;
while(tp) putchar(stk[tp--] + '0');
putchar('\n');
}
const int N=1e6+10;
int p[N],deg[N];
int flag[N];//是否含有奇数点的联通分量
int vis[N];
int n,m;
int Find(int x){
return p[x]==x?x:p[x]=Find(p[x]);
}
void unite(int x,int y){
x=Find(x);
y=Find(y);
if(x!=y)
p[y]=x;
}
int main(){
/* #ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif*/
read(n);read(m);
for(int i=1; i<=n; i++)p[i]=i;
vis[1]=1;
for(int i=1; i<=m; i++){
int u,v;
read(u);read(v);
if(u!=v){
deg[u]++; deg[v]++;
vis[u]++; vis[v]++;
unite(u, v);
}
else vis[u]++;
}
for(int i=1; i<=n; i++){
if(deg[i]&1){
flag[Find(i)]++;
}
}
int tong=0,ans=0,k=0;
for(int i=1; i<=n; i++){
if(vis[i] && Find(i)==i){
if(flag[i])ans+=flag[i];
else tong++;
k++;//计算几个联通分量
}
}
if(k==1)print(ans/2);//全图连通
else print(ans/2+tong);
return 0;
}