题意:
给出无向图,从1开始遍历,每次走两步,问最少加几条边可以
完整遍历整个图。
分析:
首先要走完整个图,那么整个图需要是连通的。那么就先添加边
使整个图连通,需要添加的边数为:当前连通块个数-1;
然后,如果图是连通的了,因为每次要走两步,如果没有环,
那么怎么走过去就会怎么走回来,那么之前因为走两步跳过的点
无论如何都遍历不到。如果是偶环,还是遍历不到。
因此只有奇数个点的环才能这样过去,回来的时候遍历到之前恰好遍历
不到的点。
因此只需要判断奇环是否存在了。如果存在奇环,那么最终答案就是连通块数目-1.
如果不存在奇环,答案还要加上1(加一条边就能连出一个奇环)。
那么如何判断奇环呢?
那就是:相邻的点已被染色,并且颜色相同,说明有奇环。
具体:
G为二分图的充要条件是G中的每一个环的长度都是偶数 。
由于二分图中不能含有奇环,所以我们只需用染色法判断是否是二分图即可;
二分图是值可以将点集分成两半,每个集合内都没有边,但可以与另一个集合中的点构成边,当且仅当一个图不存在奇数环的时候是二分图,奇数环是指环中的边数为奇数,染色法:将一个图染成只有0和1两种颜色,dfs每一个点,连通的染成不同颜色,如果发现这两个点连通且颜色相同,则说明出现了奇数环,一定不是一个二分图。
考察:
图:染色,连通块,判断是否有奇环,二分图染色
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+10;
vector<int> e[N];
int n,m,ans,v[N],flag=1;//flag=0时说明有奇环
void dfs(int x){
for(auto &it:e[x]){
if(v[it]==-1){//没被染色
v[it]=v[x]^1;//相邻的点染相反的颜色
dfs(it); //把x所在的连通块全部染色
}
else if(v[it]==v[x]) flag=0;//相邻的点已被染色,并且颜色相同,说明有奇环
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
e[x].push_back(y);
e[y].push_back(x);
}
memset(v,-1,sizeof(v));
for(int i=1;i<=n;i++){
if(v[i]==-1){
ans++;
v[i]=0;//进行染色
dfs(i);
}
}
printf("%d\n",ans-1+flag);//ans为连通块个数,那么需要添加的边数为ans-1
return 0;
}