有时候,需要以数据中的每一项为起点进行dfs或者bfs,比如寻找二分图的最大匹配问题,此时一般要借助visit数组来标记某一项是否被访问过了,避免死循环。在数据量较大的情况下,visit数组开得也比较大,多次memset整个visit数组要花很长时间。
二分图
二分图:简单来说,如果图中点可以被分为两组,并且使得所有边都跨越组的边界,则这就是一个二分图。准确地说:把一个图的顶点划分为两个不相交集 U 和V ,使得每一条边都分别连接U、V中的顶点。如果存在这样的划分,则此图为一个二分图。二分图的一个等价定义是:不含有「含奇数条边的环」的图。图 1 是一个二分图。为了清晰,我们以后都把它画成图 2 的形式。
匹配
匹配:在图论中,一个 「匹配」(matching) 是一个边的集合,其中任意两条边都没有公共顶点。例如,图 3、图 4 中红色的边就是图 2 的匹配。
我们定义匹配点、匹配边、未匹配点、非匹配边,它们的含义非常显然。例如图 3 中 1、4、5、7 为匹配点,其他顶点为未匹配点;1-5、4-7为匹配边,其他边为非匹配边。
最大匹配
最大匹配:一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配。图 4 是一个最大匹配,它包含 4 条匹配边。
如下图所示,如果在某一对男孩和女孩之间存在相连的边,就意味着他们彼此喜欢。是否可能让所有男孩和女孩两两配对,使得每对儿都互相喜欢呢?图论中,这就是完美匹配问题。如果换一个说法:最多有多少互相喜欢的男孩/女孩可以配对儿?这就是最大匹配问题。
想法分析
以下代码展示了一般的做法:
# include <bits/stdc++.h>
using namespace std;
int n, mx, a[100005], vis[100005];
void bfs(int x) {
queue<int> q;
q.push(x); vis[x] = 1;
while (!q.empty()) {
int t = q.front(); q.pop();
cout << t << " ";
if (2*t <= mx && !vis[2*t])
q.push(2*t), vis[2*t] = 1;
if (t/2 >= 1 && !vis[t/2])
q.push(t/2), vis[t/2] = 1;
}
cout << endl;
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
mx = max(mx, a[i]);
}
for (int i = 0; i < n; i++) {
memset(vis, 0, sizeof(vis));
bfs(a[i]);
}
return 0;
}
其实没有必要每次都调用memset重置vis数组,考虑用id作为搜索时是否访问过的标记,代码如下:
# include <bits/stdc++.h>
using namespace std;
int n, mx, a[100005], vis[100005];
void bfs(int c, int x) {
queue<int> q;
q.push(x); vis[x] = c;
while (!q.empty()) {
int t = q.front(); q.pop();
cout << t << " ";
if (2*t <= mx && vis[2*t] != c)
q.push(2*t), vis[2*t] = c;
if (t/2 >= 1 && vis[t/2] != c)
q.push(t/2), vis[t/2] = c;
}
cout << endl;
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
mx = max(mx, a[i]);
}
for (int i = 0; i < n; i++)
bfs(i+1, a[i]);
return 0;
}
资源传送门
- 关注【做一个柔情的程序猿】公众号
- 在【做一个柔情的程序猿】公众号后台回复 【python资料】【2020秋招】 即可获取相应的惊喜哦!
- 自己搭建的博客地址:梦魇回生的博客
「❤️ 感谢大家」
- 点赞支持下吧,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓 -_-)
- 欢迎在留言区与我分享你的想法,也欢迎你在留言区记录你的思考过程