题目描述
小sun最近为了应付考试,正在复习图论,他现在学到了图的遍历,觉得太简单了,于是他想到了一个更加复杂的问题:
无向图有n个点,从点1开始遍历,但是规定:按照每次“走两步”的方式来遍历整个图。可以发现按照每次走两步的方法,不一定能够遍历整个图,所以现在小sun想问你,最少加几条边,可以完整的遍历整个图。
输入描述:
第一行两个整数n,m代表图的点数和边数。
接下来m行,每行两个整数u,v代表u,v有边相连(无向边)
输出描述:
输出一行,代表最少要添加的边数。
思路:
先不考虑走两步,就先考虑要走完整个图,那么整个图肯定是需要联通的,那么我们需要求出有多少个连通块,需要加的边数就是连通块个数-1,这样先保证图联通。
图联通后再考虑走两步的走法,可以简单画一下,如果图联通,只要存在奇数环,那么就可以走完整个图,因为奇数环,从环的起点走到环的终点后,从终点继续走另一边到起点,这样之前遍历的是奇数点,第二次过来就变成了偶数点,不太明白的话,可以画图理解下。
那么如果没有奇数环呢?只需要在其中一个连通块中,加上一条边形成奇数环即可。
所以整个题等价求有多少个联通块、求是否存在奇数环
那么可以通过dfs ,O(m+n)复杂度即可解决。
#include<bits/stdc++.h> using namespace std; vector<int> e[100005]; int ans,flag=1;///flag=1 表示不存在奇数环 int v[100005]; void dfs(int x){ for(auto &it:e[x]){ if(v[it]==-1){//没被染色 v[it]=v[x]^1;///相邻的点染相反的颜色 dfs(it); } else if(v[it]==v[x]) flag=0;//相邻的点已被染色,并且颜色相同 说明有奇环 } } int main(){ int n,m;cin>>n>>m; for(int i=1;i<=m;i++){ int x,y;cin>>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); } } cout<<ans-1+flag; return 0; }