四个点的圈是二分图吗

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;

以上是关于四个点的圈是二分图吗的主要内容,如果未能解决你的问题,请参考以下文章

完全二分图生成树计数

Hall定理 二分图完美匹配

某某天的模拟赛题 deadline(二分图)

二分图定理及证明

[模板] 二分图/网络流相关定理

随机二分图