四个点的圈是二分图吗
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了四个点的圈是二分图吗相关的知识,希望对你有一定的参考价值。
二分图(概念、相关算法和题目应用)(全面整理) 原创2021-12-24 18:49:02
37点赞
阐上
码龄2年
关注
TP
二分图的概念:
二分图常用算法:
染色法(判断一个图是否为二分图):
匈牙利算法(求出二分图的最大匹配数):
相应题目应用:
二分图 染色 应用:
Acwing:关押罪犯
二分图最大匹配应用:
Acwing:棋盘覆盖
洛谷:矩阵游戏
二分图最大匹配的一些推论:
二分图最小点覆盖应用:
Acwing:机械任务
Acwing:泥地
二分图最大独立集应用:
Acwing:骑士放置
二分图 最大路径点覆盖 与 最大路径重复点覆盖 应用:
Acwing:捉迷藏
二分图的概念:
二分图通常针对 无向图 问题(有些题目虽然是有向图,但一样有二分图性质)
在一张图中,如果能够把全部的点分到 两个集合 中,保证两个集合内部没有 任何边 ,图中的边 只存在于两个集合之间,这张图就是二分图
——————————————————————————————————————————
二分图常用算法:
染色法(判断一个图是否为二分图):
算法原理就是,用 黑 与 白 这两种颜色对图中点染色(相当于给点归属一个集合),一个点显然不能同时具有两种颜色,若有,此图就不是二分图
在这里插入图片描述
在这里插入图片描述
代码:
bool dfs(int u, int c)
color[u] = c;//当前点先染色
for (int i = h[u]; ~i; i = ne[i])
int j = e[i];//对于这个点连接的所有的点
if (color[j]) //如果已经被染过色了
if (color[j] == c)return false;
//就需要判断一下,如果两点颜色一样,染色就冲突了
else if (!dfs(j, 3 - c))return false;
//否则dfs去染下一个结点,赋予的颜色肯定要跟 c 不一样
//3 - 1 == 2,3 - 2 == 1
//同时传回染色成功与否的信息
return true;
bool check()
memset(color, 0, sizeof color);//0 —— 未染色,1 —— 黑色,2 —— 白色
for (int i = 1; i <= n; i++)
if (color[i] == 0)//一旦某个点没染过色,dfs去染色
if (!dfs(i, 1))return false;//如果传回false显然失败,此图不是二分图
return true;
//否则true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
遍历了这张图的点和边,时间复杂度O ( n + m ) O(n + m)O(n+m)
——————————————————————————————————————————
匈牙利算法(求出二分图的最大匹配数):
满足 是二分图 这个前提,才能使用匈牙利算法
所谓 最大匹配数 的意思就是:
两个集合分别选一个点,这两个点之间有边就确认一段关系(一个集合中的两点 占有 另一集合中同一个点 是不合法的 一夫一妻(确信) ),最多的关系数量就是这张二分图的最大匹配
在这里插入图片描述
代码:
bool find(int x) //标准匈牙利
for (int j = 1; j <= n; j++)
if (!st[j] && g[x][j])
//x 点连向的所有点(因为是二分图,所以这些点都在右集合),如果存在边且没标记过
st[j] = true;
//标记一下,防止多次遍历
int t = match[j];
//右集合中该点的匹配对象
if (!t || find(t))
//没对象就可以和 x 匹配,有的话就让 t 尝试更改对象,能更改就和 x 匹配,不能就false
match[j] = x;
return true;
return false;
int main()
cin >> n;
int ans = 0;
for (int i = 1; i <= n; i++) //遍历左集合
memset(st, 0, sizeof st);//每次都要重置标记
if (find(i))ans++;//一旦有一个匹配,数量就++
cout << ans;
return 0;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
最坏情况会每个点遍历全部边一次,所以时间复杂度是O ( n m ) O(nm)O(nm)
但匈牙利算法还是很优秀的,大部分情况时间都比较小
如果想要更优秀的算法左转 网络流 吧,匈牙利匹配本质上还是网络流的一种特殊形式,网络流可以更好地解决此类问题。网络流真是太简单了bushi
——————————————————————————————————————————
相应题目应用:
二分图 染色 应用:
Acwing:关押罪犯
在这里插入图片描述
题意又臭又长,总结就是:
尽可能将 仇恨值大的 两名罪犯放在不同监狱中
把仇恨值当作罪犯之间的边的边权,两座监狱看作两个集合
这道题就变成了 如何让二分图 两集合之间的边权 尽可能大(使得集合内部边权尽可能小,冲突也就没那么激烈)
观察数据范围,此题可以用 二分 + 染色法 求解
代码:
#include<bits/stdc++.h>
#include<unordered_set>
#include<unordered_map>
#define mem(a,b) memset(a,b,sizeof a)
#define cinios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define sca scanf
#define pri printf
#define ul (u << 1)
#define ur (u << 1 | 1)
#define fx first
#define fy second
//#pragma GCC optimize(2)
//[博客地址](https://blog.csdn.net/weixin_51797626?t=1)
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 20010, M = 200010, MM = 3000010;
int INF = 0x3f3f3f3f, mod = 100003;
ll LNF = 0x3f3f3f3f3f3f3f3f;
int n, m, k, T, S, D;
int h[N], e[M], ne[M], w[M], idx;
int color[N];//染色,记录该点的状态:0 —— 未染色、1 —— 染白、2 —— 染黑
void add(int a, int b, int x)
e[idx] = b, w[idx] = x, ne[idx] = h[a], h[a] = idx++;
bool dfs(int u, int c, int mid)
color[u] = c;//染该点
for (int i = h[u]; ~i; i = ne[i])
int j = e[i];
if (w[i] <= mid)continue;
//mid是答案,是设定的集合内部最大边权,如果该边小于等mid,边两点显然可以随意处理,放在一个集合内也行
//否则,该边两点就严格要放在两个不同集合,防止冲突
if (color[j]) //连向的点若被染过色
if (color[j] == c)return false;//显然色不能相同
else if (!dfs(j, 3 - c, mid))return false;//没被染过就染该点
return true;//成功
bool check(int mid)
mem(color, 0);//每次重新对点染色
for (int i = 1; i <= n; i++)
if (color[i] == 0)//枚举每一个点是因为有可能有多个连通块
if (!dfs(i, 1, mid))return false;//要保证都为二分图
return true;
int main()
cinios;
cin >> n >> m;
mem(h, -1);
for (int i = 0; i < m; i++)
int a, b, x;
cin >> a >> b >> x;
add(a, b, x);//二分图通常针对的都是无向图
add(b, a, x);
int l = 0, r = 1e9;//二分答案
while (l < r)
int mid = l + r >> 1;//mid 即是最小的最大影响力
//比 mid 小的影响显然都可以随意塞到监狱中
//比 mid 大的影响要让其尽可能不存在(即一边两点分别在图的两个不同子集中)
//也就是询问能否用比 mid 大的边建立一个二分图
if (check(mid))r = mid;//能的话显然包含答案
else l = mid + 1;//不能就要增大,右移往答案靠拢
cout << l;
return 0;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
——————————————————————————————————————————
二分图最大匹配应用:
Acwing:棋盘覆盖
在这里插入图片描述
看上去像状压dp,但一看数据范围2 100 2^1002
100
状压肯定炸,所以探讨一下是否具有二分图的性质
如果把每个格子看成点,放置骨牌(长2宽1)的操作可 参考技术A 若图G的结点集V(G)可以分成两个非空子集 V1和V2,并且图G的任意边xy关联的两个结点 x、y分别属于这两个子集,则图G是二分图。
所有的树都是二分图。从树中任取一个结点为树根,着上白色,然后将根的所有孩子着上黑色,将下一层再着白色,继续此过程直到所有结点都着色。
用着色法或标记法可以检测一个给定的图是不是十分图。
偶数个结点的圈是二分图,而奇数个结点的圈不是二分图。所以如果图中含有奇圈,就不是二分图。其实这是一个定理: 图G是二分图当且仅当图G不含奇圈。 参考技术B 是的,偶数个结点的圈是二分图,而奇数个结点的圈不是二分图。所以如果图中含有奇圈,就不是二分图。
CCSP子图计数
思路: 直接暴力枚举,可以拿到80分。枚举出四个点的所有组合,检查是否满足条件(四个点为CCSP集合)。如果满足再根据入度算出其贡献,该4个点的贡献=入度为三的点个数*(4个点之间的边数-3),所有4个点的组合加起来的贡献即为答案。
至于贡献为啥这样算,拿出笔来再纸上比划两下很容易看出的。
Code:
#include<iostream>
#include<map>
using namespace std;
string str;
int tu[105][105];
map<char, int> ccsp;
int lst[10];
int check()//4个点的贡献
map<int, int> ma;
map<char, int> cc;
int sum = 0;//4个点总的边数
int flag = 0;
for (int i = 1;i <= 4;i++)
cc[str[lst[i]]]++;
for (int j = i + 1;j <= 4;j++)
if (tu[lst[i]][lst[j]])
ma[lst[i]]++;
ma[lst[j]]++;
sum++;
int san = 0;//度为3的点的个数
for (auto i : ma)
if (i.second == 3)
san++;
if (cc != ccsp || san == 0)return 0;
return san * (sum - 3);
int main()
int n, m;
cin >> n >> m;
cin >> str;
str.insert(str.begin(), '%');//单纯占一个位置,让str[1]对应题目给出的字符串
int t = m;
while (t--)
int a, b;
cin >> a >> b;
tu[a][b] = 1;
tu[b][a] = 1;
int ans = 0;
ccsp['C'] += 2;
ccsp['S']++;
ccsp['P']++;
for (int a = 1;a <= n;a++)
for (int b = a + 1;b <= n;b++)
for (int c = b + 1;c <= n;c++)
for (int d = c + 1;d <= n;d++)
lst[1] = a;
lst[2] = b;
lst[3] = c;
lst[4] = d;
ans+=check();
cout << ans;
以上是关于四个点的圈是二分图吗的主要内容,如果未能解决你的问题,请参考以下文章