题目
题目描述:小sun最近为了应付考试,正在复习图论,他现在学到了图的遍历,觉得太简单了,于是他想到了一个更加复杂的问题:
无向图有n个点,从点1开始遍历,但是规定:按照每次“走两步”的方式来遍历整个图。
可以发现按照每次走两步的方法,不一定能够遍历整个图,所以现在小sun想问你,最少加几条边,可以完整的遍历整个图。
第一行两个整数n,m代表图的点数和边数。
接下来m行,每行两个整数u,v代表u,v有边相连(无向边)
输出一行,代表最少要添加的边数。
解析
首先这道题和题目说的一样就是一道简单的图的遍历:
- 不过这道题的特点就是有个特殊条件:每次走两步,问我们能不能走完这张图。
我们先来看题:
- 题目虽然叫我们遍历图了,但是一下就能发现,他并没有说走过的节点不能再走,所以中间节点是可以走走过的地方的。
- 我们都知道一条路下去,如果两步两步一走,绝对是不可能遍历所有节点的,假如起点是偶数节点的话,那你只能走完所有偶数节点。
- 为了能有变换,我们就一定要有环结构,才能让奇偶产生变换,而且必须是奇数环。因为奇数环走一圈下来奇偶就换了,而偶数环却不行。
- 所以题目就变成了判定有没有奇数环,没有的话加一条边就能造出来。
- 这里我们484漏了什么?!
- 对了!题目没有告诉我们这到底是不是连通图啊!!所以我们还要加边让这个变成一整张连通图(多一张加一条边)。
- 所以题目就变成了:判断信息中有多少张连通图与是否有奇数环。
连通图判定:
- 这个简单,因为每一次dfs都可以遍历一张图,我们对每个点进行dfs(如果这个点被以前的点遍历过就不进行了)。
- 遍历过怎么记呢?加一个visited观测数组做标记就好啦。
奇数环判定:
- 奇数环的特点是什么呢?当然是奇数咯(废话。
- 奇数又有什么特点,这里就要用到经验了,我们可以用一个双色数组标记奇偶节点。
- 判定环的奇偶就只要判定遍历完环的时候,相邻的两个点的颜色是否相同就可以了(奇数就是相同,偶数是不同)
- 栗子:奇数环假设就是101,首尾相同对不对。偶数环假设是1010,首尾不同对不对。
然后就是打代码了呗:
- 首先我们用前向星保存图(老方法了)。
- 然后对每个点(要判定以前有没有被遍历过哦)进行dfs。
- 最后输出就是联通图数量 - 1 + 是不是奇数环(是就+0,不是就+1构成奇数环)。
- 看我代码呗~
AC代码
#include <iostream>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//代码预处理区
const int MAX = 1e5 + 7;
int head[MAX], tot;
struct Node {
int v, next;
} edge[MAX << 1];
int n, m, ans;//n个点,m条边,加ans条边
bool visited[MAX], color[MAX], flag;//标记数组,奇偶判定数组,奇环判定变量
//全局变量区
void add_edge(int u, int v);
void dfs(int u, int fa);
//函数预定义区
int main() {
IOS;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int u, v; cin >> u >> v;
add_edge(u, v);
add_edge(v, u);
}
for (int i = 1; i <= n; i++)
if (!visited[i]) {
ans++;
color[i] = true;
dfs(i, 0);
}
cout << ans - 1 + !flag << endl;
return 0;
}
void add_edge(int u, int v) {
tot++;
edge[tot].v = v;
edge[tot].next = head[u];
head[u] = tot;
}
void dfs(int u, int fa) {
visited[u] = 1;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].v;
if (!visited[v]) {
color[v] = !color[u];
dfs(v, u);
}
else
if (color[v] == color[u]) flag = true;//奇环判定为true
}
}
//函数区
京公网安备 11010502036488号