二分图模型即状态整理

二分图首先是个无向图。

主要有以下几类问题:

1.二分图,不存在奇数环,染色法不存在矛盾

2.匈牙利算法,匹配,最大匹配,匹配点,增广路径

3.最小点覆盖,最大独立集,最小路径点覆盖,最小路径重复点覆盖

        最大匹配数=最小点覆盖=总点数-最大独立集=总点数-最小路径覆盖

(以下两种一般不用二分图来写)

4.最优匹配(达到最大匹配的情况下,边权和的最大值),KM算法(最小费用流)

5.多重匹配(每个点可以匹配多个点)(最大流)

1.二分图 = 不存在奇数环 = 染色法不存在矛盾

证明比较简单,可以自行搜索。

染色法模板:


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])//如果j已经染色,判断是否有矛盾{if (color[j] == c) return false;//如果有矛盾那么判否}else //如果没有染色,那么对j染色,判断往下染的时候是否会出现矛盾{if (dfs(j, 3 - c)) return false;}}return true;
}

257. 关押罪犯(257. 关押罪犯 - AcWing题库)

思路:首先我们注意到,需要将所有罪犯分到两个监狱中去,那么联想到二分图,二分图的要求就是边全部在两个集合之间,集合内部没有边,但是这里显然不能保证图是符合要求的,但是我们要求的是最大值最小,所以考虑二分,对于一个二分值,如果小于等于这个二分值的可以分进一个监狱,否则就必须分进两个监狱,那么从二分图的角度来看,相当于只保留了大于二分值的边。那么我们只需要判断这些被保留的边是否可以构成一个二分图,就可以判断当前的二分值是否有效了。如果可以构成二分图,显然这么分是可以的,如果不能构成二分图,那么就需要将二分值往上调一调,因为这样的话留下的边就更少了,产生矛盾的概率也更小了。

#include<bits/stdc++.h>
using namespace std;
const int N=20010,M=200010;
int h[N],e[M],ne[M],w[M],idx;
int n,m;
int color[N];
void add(int a,int b,int c)
{w[idx]=c,e[idx]=b,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;if(color[j]){if(color[j]==c) return 0;}else {if(!dfs(j,3-c,mid)) return 0;}}return 1;
}
bool check(int mid)
{memset(color,0,sizeof color);for(int i=1;i<=n;i++){if(!color[i]){if(!dfs(i,1,mid)) return 0;}}return 1;
}
int main()
{scanf("%d%d",&n,&m);memset(h,-1,sizeof h);for(int i=1;i<=m;i++){int a,b,c;scanf("%d%d%d",&a,&b,&c);add(a,b,c),add(b,a,c);}int l=0,r=1e9;while(l<r){int mid=(l+r)/2;if(check(mid)) r=mid;else l=mid+1;}cout<<l;
}

2.匈牙利算法,匹配,最大匹配,匹配点,增广路径

匈牙利算法就是从二分图中查找最大匹配的算法。

匹配指二分图中一组没有公共点的边;

最大匹配是指边的数量最多的一组匹配

匹配点指的是在匹配中的点

增广路径:从一个非匹配点沿着非匹配边、匹配边、非匹配边、匹配边、...,最后走到另一个集合中的一个非匹配点。得到的路径是一个增广路径。增广路径是可以取代当前的匹配。

最大匹配等价于不存在增广路径

 找最大匹配用的是匈牙利算法,时间复杂度最坏O(nm),但一般不会到这个值。

匈牙利算法的实现思路是:遍历一个点集中的每个点,为每个点找一个匹配,如果找到的点还没有被匹配过,那么就将找到的点的匹配记为当前点,否则就去看能否为这个点的匹配点找一个新的匹配,注意新的匹配不能是这个点,所以在搜到这个点的时候需要标记以下。每一次开始查找的时候所有点都是没有标记的,标记只是在这一轮查找中这个点不能用,但是下一轮就可以用了。因为从一个点集找,所以我们建单向边即可。

861. 二分图的最大匹配(活动 - AcWing)

#include<bits/stdc++.h>
using namespace std;
const int N=1010,M=100010;
int n1,n2,m;
int h[N],e[M],ne[M],idx;
int st[M],match[M];
void add(int a,int b)
{e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool find(int u)
{for(int i=h[u];~i;i=ne[i]){int j=e[i];if(st[j]) continue;st[j]=1;if(!match[j]||find(match[j])) {match[j]=u;//这里要记得分配return 1;}}return 0;
}
int main()
{scanf("%d%d%d",&n1,&n2,&m);memset(h,-1,sizeof h);while(m--){int a,b;scanf("%d%d",&a,&b);add(a,b);}int c=0;for(int i=1;i<=n1;i++){memset(st,0,sizeof st);if(find(i)) c++;}cout<<c;
}

372. 棋盘覆盖(活动 - AcWing) 

思路:这里可能会想到用状态压缩来做,但是N太大了,2^100,一定会超时。所以我们需要换个思路。

这题其实有个特别巧妙地地方,我们将横纵坐标之和为奇数地称为奇点,为偶数的称为偶点,仔细观察可以放的位置,占据的都是连续的两个点,或横或竖,也就是一个奇点一个偶点,再加上不能重复,那么不就是在奇点和偶点两个集合之间找最大匹配嘛,分析到这里就很简单了,我们从奇点集去找偶点集的匹配即可。

#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef pair<int,int> pii;
const int N=120;
int n,m;
pii match[N][N];
int g[N][N],st[N][N];
int dx[]={0,0,-1,1};
int dy[]={1,-1,0,0};
bool find(int x,int y)
{for(int u=0;u<4;u++){int a=x+dx[u],b=y+dy[u];if(a<=0||a>n||b<=0||b>n) continue;if(g[a][b]||st[a][b]) continue;st[a][b]=1;pii t=match[a][b];if(t.x==-1||find(t.x,t.y)) {match[a][b]={x,y};return 1;}}return 0;
}
int main()
{scanf("%d%d",&n,&m);while(m--){int a,b;scanf("%d%d",&a,&b);g[a][b]=1;}memset(match,-1,sizeof match);int c=0;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if((i+j)%2==0 || g[i][j]) continue; memset(st,0,sizeof st);if(find(i,j)) c++;}}cout<<c;
}

 3.最小点覆盖,最大独立集,最小路径点覆盖,最小路径重复点覆盖

结论:最大匹配数=最小点覆盖=总点数-最大独立集=总点数-最小路径覆盖

最小点覆盖:选出最少的点,使得每条边的两个端点中至少有一个点被选出来了。

376. 机器任务(376. 机器任务 - AcWing题库)

思路:每个任务有两种完成的方式,那么如果在两种方式之间连一条边,那么就会得到一个二分图,相当于要求这个二分图的最小点覆盖。因为任意一条边至少有一个端点要被选。

对了要注意一下,初始为0,所以如果a或者b为0的话,可以直接执行,不用算到后面重启中去。

#include<bits/stdc++.h>
using namespace std;
const int N=120,M=1010;
int n,m,k;
int h[N],e[M],ne[M],idx;
int st[N],match[N];
void add(int a,int b)
{e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool find(int u)
{for(int i=h[u];~i;i=ne[i]){int j=e[i];if(st[j]) continue;st[j]=1;if(match[j]==-1||find(match[j])){match[j]=u;return 1;}}return 0;
}
int main()
{while(~scanf("%d",&n)){if(!n) break;scanf("%d%d",&m,&k);memset(h,-1,sizeof h);memset(match,-1,sizeof match);while(k--){int c,a,b;scanf("%d%d%d",&c,&a,&b);if(!a||!b) continue;//最初为0add(a,b);}int c=0;for(int i=1;i<=n;i++){memset(st,0,sizeof st);if(find(i))c++;}printf("%d\n",c);}}

 最大独立集:从图中选出最多的点,使得任意两点之间没有边

最大团:从图中选出最多的点,使得任意两点之间都有边

在二分图中,求最大独立集 => 去掉最少的点将所有边都覆盖点 <=> 最小点覆盖 <=> 最大匹配

最大独立集=n-最大匹配

378. 骑士放置(378. 骑士放置 - AcWing题库)

这里不能互相攻击到,那么我们将可以互相攻击的点之间连边,得到一张图,那么需要求的就是最大独立集,因为选出的点两两之间没有边。然后还需要确定的一个问题就是这张图是否是二分图。对于一个点,它能攻击到的点与它的横纵左边之间的总差值应该是一个奇数,那么就会改变奇偶性,那么我们按照奇点偶点分成两个集合,那么就是一个二分图。类似于棋盘覆盖那道题,问题就解决了。

#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef pair<int,int> pii;
const int N=120;
int n,m,t;
pii match[N][N];
int g[N][N],st[N][N];
int dx[]={1,1,-1,-1,2,2,-2,-2};
int dy[]={2,-2,2,-2,1,-1,1,-1};
bool find(int x,int y)
{for(int u=0;u<8;u++){int a=x+dx[u],b=y+dy[u];if(a<1||a>n||b<1||b>m) continue;if(g[a][b]||st[a][b]) continue;st[a][b]=1;pii t=match[a][b];if(t.x==-1||find(t.x,t.y)) {match[a][b]={x,y};return 1;}}return 0;
}
int main()
{scanf("%d%d%d",&n,&m,&t);for(int i=1;i<=t;i++){int a,b;scanf("%d%d",&a,&b);g[a][b]=1;}memset(match,-1,sizeof match);int c=0;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if((i+j)%2 || g[i][j]) continue; memset(st,0,sizeof st);if(find(i,j)) c++;}}cout<<n*m-t-c;//坏点是不能被选的,所以不包含在图中,我们是要从图中选点,图中的总点数-最大匹配数
}

 最小(路径)点覆盖:在DAG(有向无环图)中用最少的互不相交(点边都不重复)的路径将所有点覆盖住。

这里需要用到拆点:对于原来的每个点建立一个新点,原点作为出点,新点作为入点。那么相当于就产生一个二分图。那么最小点覆盖=n(一边的点数)-m(最大匹配数)

因为互不相交,所以每个点只有一个出度和一个入度,那么在新图中就对应一个匹配。

路径的终点对应左侧一个非匹配点,那么一个非匹配点就对应一条路径。

所以求最小路径数,就是求左侧最少非匹配点的数量,那么匹配点最多,那么就是最大匹配。

如果可以相交,那么就是最小路径重复点覆盖。

求原有向图的传递闭包得到一个新图,求新图的最小路径点覆盖。

379. 捉迷藏(379. 捉迷藏 - AcWing题库)

相当于给定一个有向无环图,从中选出一些点,使得任意两点之间都不能从一个走到另一个。

我们假设图中有m个最小路径重复点覆盖,那么显然我们只能在一条路径上选一个点,否则选的点就是可达的。因为这里只要可达就可以相互看到,所以需要求最小路径重复点覆盖。

传递闭包可以用Floyd算法来求。

#include<bits/stdc++.h>
using namespace std;
const int N=210;
int n,m;
int st[N],g[N][N],match[N];
int find(int u)
{for(int i=1;i<=n;i++){if(g[u][i]==0) continue;if(st[i]) continue;st[i]=1;if(!match[i]||find(match[i])){match[i]=u;return 1;}}return 0;
}
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int a,b;scanf("%d%d",&a,&b);g[a][b]=1;}for(int k=1;k<=n;k++){for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){g[i][j]|= g[i][k]&g[k][j];}}}int res=0;for(int i=1;i<=n;i++){memset(st,0,sizeof st);if(find(i)) res++;}cout<<n-res;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/694025.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在VS里使用C#制作窗口应用

新建项目 创建项目的时候搜索net&#xff0c;选择这个。 打开应该是这样 第一个控件 选择公共控件 - PictureBox - 拖入Form 在Image处选择上传本地资源&#xff0c;建议上传一个小一点的图片。 修改一下尺寸。 ctrls 保存 从“属性”切换到“事件” 双击Click事件…

DBSCAN密度聚类介绍 样本点 样本集合 半径 邻域 核心对象 边界点 密度直达 密度可达 密度相连

DBSCAN密度聚类介绍 样本点 样本集合 半径 邻域 核心对象 边界点 密度直达 密度可达 密度相连 简介概念定义原理DBSCAN的优点DBSCAN的缺点小尝试制作不易&#xff0c;感谢三连&#xff0c;谢谢啦 简介 DBSCAN&#xff08;Density-Based Spatial Clustering of Applications wi…

【算法】动态规划1,最小花费爬楼梯,解码方法

一、动态规划简介 动态规划 , 英文名称 Dynamic Programming , 简称 DP , 不是具体的某种算法 , 是一种算法思想 ; 动态规划 , 没有具体的步骤 , 只有一个核心思想 ; 动态规划 的 核心思想 是 由大化小 , 大规模问题 使用 小规模问题 计算结果 解决 , 类似于 分治算法 ; 二、…

srs集群下行edge处理逻辑

官方关于源站集群的介绍&#xff1a; Origin Cluster | SRS 下行边缘是指观众端从边缘edge拉流&#xff0c;边缘edge回源到源站origin节点拉流&#xff0c;然后再 把流转给客户端 边缘处理类SrsPlayEdge 当服务器收到播放请求时&#xff0c;创建对应的consumer消费者。在创…

Docker后台启动镜像,如何查看日志信息

执行 docker run -d -p 9090:8080 core-backend-image 命令后&#xff0c;Docker 会在后台运行一个新的容器实例&#xff0c;并映射宿主机的 9090 端口到容器的 8080 端口。要查看启动的容器日志&#xff0c;您需要先获取容器的 ID 或名称&#xff0c;然后使用 docker logs 命令…

Linux系统之iptables应用SNAT与DNAT

一、SNAT&#xff1a; 1.应用环境 局域网主机共享单个公网IP地址接入Internet &#xff08;私有IP不能在Internet中正常路由&#xff09; 2.SNAT原理 源地址转换&#xff0c;根据指定条件修改数据包的源IP地址&#xff0c;通常被叫做源映谢数据包从内网发送到公网时&#x…

Fiddler与wireshark使用

Fiddler解决三个问题 1、SSL证书打勾&#xff0c;解析https请求 2、响应回来乱码&#xff0c;不是中文 3、想及时中止一下&#xff0c;查看实时的日志 4、搜索对应的关键字 问题1解决方案&#xff1a; 标签栏Tools下 找到https&#xff0c;全部打勾 Actions里面 第一个 t…

从输入url到页面显示中间发生了什么

文章目录 整体概述URL释义用户输入缓存处理域名解析IP 地址什么是域名解析浏览器查找域名对应IP小结 TCP 三次握手握手时序三次握手数据包分析为什么需要三次握手 HTTP 请求HTTP 响应服务器MVC 后台处理阶段http 响应报文 TCP 四次挥手浏览器渲染 整体概述 浏览器输入 URL 到页…

如何搭建Facebook直播网络?

在当今数字化时代&#xff0c;Facebook直播已经成为了一种极具吸引力的社交形式&#xff0c;为个人和企业提供了与观众直接互动的机会&#xff0c;成为推广产品、分享经验、建立品牌形象的重要途径。然而&#xff0c;对于许多人来说&#xff0c;搭建一个稳定、高质量的 Faceboo…

创意办公:专注 ONLYOFFICE,探索办公新境界

一.ONLYOFFICE 介绍 ONLYOFFICE 是一个基于 Web 的办公套件&#xff0c;提供了文档处理、电子表格和演示文稿编辑等功能。它被设计为一个协作工具&#xff0c;支持多人实时协作编辑文档&#xff0c;并且可以在本地部署或者作为云服务使用。 二.ONLYOFFICE 特点和功能 以下是 …

品牌渠道管控的目标是什么

品牌做渠道管控的根本原因是解决渠道中的各种问题&#xff0c;常见的渠道问题包含破价、窜货、假货等&#xff0c;在治理渠道的过程中&#xff0c;其实也是对渠道中各角色关系的梳理&#xff0c;比如通过治理破价链接&#xff0c;可以及时发现渠道中不符合品牌价值的经销商&…

十大基础排序算法

排序算法分类 排序&#xff1a;将一组对象按照某种逻辑顺序重新排列的过程。 按照待排序数据的规模分为&#xff1a; 内部排序&#xff1a;数据量不大&#xff0c;全部存在内存中&#xff1b;外部排序&#xff1a;数据量很大&#xff0c;无法一次性全部存在内存中&#xff0c;…

Vue2尚品汇前台项目笔记——(1)项目初始化

Vue2尚品汇前台项目笔记 一、项目初始化 使用[脚手架创建项目&#xff0c;具体参考之前的脚手架配置笔记&#xff0c;我起名叫vue_shop_test 1.脚手架目录分析 node_modules文件夹&#xff1a;项目依赖文件夹 public文件夹&#xff1a;一般放置一些静态资源&#xff08;图…

Java项目:20 基于SSM实现的支教管理系统

作者主页&#xff1a;源码空间codegym 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 ssm支教管理系统&#xff08;前台后台&#xff09; 前台角色&#xff1a;支教学校志愿者 支教学校功能模块&#xff1a;支教学校查询报名职位…

【数据结构】单向循环链表

一、mian函数 #include <stdio.h> #include "./3.looplinklist.h" int main(int argc, const char *argv[]) {looplinklist* head create_looplinklist();insertHead_looplinklist(head,100);insertHead_looplinklist(head,200);insertHead_looplinklist(hea…

瑞盟MS5188N——16bit、8 通道、500kSPS、 SAR 型 ADC

产品简述 MS5188N 是 8 通道、 16bit 、电荷再分配逐次逼近型模数 转换器&#xff0c;采用单电源供电。 MS5188N 拥有多通道、低功耗数据采集系统所需的所有 组成部分&#xff0c;包括&#xff1a;无失码的真 16 位 SAR ADC &#xff1b;用于将输入配 置为单端输入…

【Flink状态管理(八)】Checkpoint:CheckpointBarrier对齐后Checkpoint的完成、通知与对学习状态管理源码的思考

文章目录 一. 调用StreamTask执行Checkpoint操作1. 执行Checkpoint总体代码流程1.1. StreamTask.checkpointState()1.2. executeCheckpointing1.3. 将算子中的状态快照操作封装在OperatorSnapshotFutures中1.4. 算子状态进行快照1.5. 状态数据快照持久化 二. CheckpointCoordin…

图的遍历-----深度优先遍历(dfs),广度优先遍历(bfs)【java详解】

目录 简单介绍&#xff1a;什么是深度、广度优先遍历&#xff1f; 深度优先搜索&#xff08;DFS&#xff0c;Depth First Search&#xff09;&#xff1a; 大致图解&#xff1a; 广度优先搜索&#xff08;BFS&#xff0c;Breadth First Search&#xff09;&#xff1a; 大致图…

Python学习笔记——自定义函数(传递任意数量的实参)

Python允许函数从调用语句中收集任意数量的实参。例如下面自定义函数制作一个披萨&#xff0c;它需要接受很多配料&#xff0c;但无法预先确定顾客要点多少种配料。 下面行数只有一个形参*toppings&#xff0c;不管调用语句提供多少个实参&#xff0c;这个参数都会收集到&…

用 LangChain 和 Milvus 从零搭建 LLM 应用

如何从零搭建一个 LLM 应用&#xff1f;不妨试试 LangChain Milvus 的组合拳。 作为开发 LLM 应用的框架&#xff0c;LangChain 内部不仅包含诸多模块&#xff0c;而且支持外部集成&#xff1b;Milvus 同样可以支持诸多 LLM 集成&#xff0c;二者结合除了可以轻松搭建一个 LL…