题目描述
小sun最近为了应付考试,正在复习图论,他现在学到了图的遍历,觉得太简单了,于是他想到了一个更加复杂的问题:

无向图有n个点,从点1开始遍历,但是规定:按照每次“走两步”的方式来遍历整个图。可以发现按照每次走两步的方法,不一定能够遍历整个图,所以现在小sun想问你,最少加几条边,可以完整的遍历整个图。

输入描述:
第一行两个整数n,m代表图的点数和边数。

接下来m行,每行两个整数u,v代表u,v有边相连(无向边)
输出描述:
输出一行,代表最少要添加的边数。

题解
保证整个图肯定是需要联通的,那么我们需要求出有多少个连通块,需要加的边数就是连通块个数-1。
如果图联通,只要存在奇数环,那么就可以走完整个图,因为奇数环,从环的起点走到环的终点后,从终点继续走另一边到起点,这样之前遍历的是奇数点,第二次过来就变成了偶数点。其实就是判断这个图是不是二分图,用染色法即可。如果没有奇数环,只需要在其中一个连通块中,加上一条边形成奇数环即可。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=100005;
vector<int> v[N];//图的表示
int n,m,color[N],f;
void dfs(int x,int c){
    color[x]=c;//顶点x染成c
    for (int i = 0; i < v[x].size(); ++i){
        if(color[v[x][i]]==c)f=0;
        else if(!color[v[x][i]])dfs(v[x][i],-c);
    }
}
void solve(){
    //如果是连通图,则一次dfs即可访问所有位置
    int k=0,num=0;
    for (int i = 1; i <= n; ++i){
        if (color[i]==0){
            f=1;
            dfs(i,1);
            if(!f)k=1;
            num++;
        }
    }
    if(k)cout<<num-1;
    else cout<<num;
}
int main(){
    cin>>n>>m;
    for(int i=0;i<m;i++){
        int x,y;cin>>x>>y;
        v[x].push_back(y),v[y].push_back(x);
    }
    solve();
    return 0;
}