二分图、匈牙利算法

news2024/11/25 15:31:02

目录

一,二分图

CodeForces 687A NP-Hard Problem

力扣 785. 判断二分图

二,完全二分图

1,完全二分图

2,K2,3

3,K3,3

三,匈牙利算法

1,二分图最大匹配

2,其他图论问题


一,二分图

一般我们研究二分图默认指的是无向图。

二分图是指,可以把图中的顶点分成两个集合,使得每个集合中的所有点之间都是互不相连的。

	//判断是不是二分图, 节点编号是0到n-1
	static bool isTwoDivGraph(int n, UndirectedGraphData<int> &g) {
		UnionDif unions(n);
		for (auto e : g.edges) {
			unions.find(e.a);
			unions.find(e.b);
			if (unions.inSame(e.a, e.b)) {
				if (int(unions.getDif(e.a) + unions.getDif(e.b)) % 2)return false;
			}
			else {
				unions.merge(e.a, e.b,1);
			}
		}
		return true;
	}

CodeForces 687A NP-Hard Problem

题目:

Description

Recently, Pari and Arya did some research about NP-Hard problems and they found the minimum vertex cover problem very interesting.

Suppose the graph G is given. Subset A of its vertices is called a vertex cover of this graph, if for each edge uv there is at least one endpoint of it in this set, i.e.  or  (or both).

Pari and Arya have won a great undirected graph as an award in a team contest. Now they have to split it in two parts, but both of them want their parts of the graph to be a vertex cover.

They have agreed to give you their graph and you need to find two disjoint subsets of its vertices A and B, such that both A and B are vertex cover or claim it's impossible. Each vertex should be given to no more than one of the friends (or you can even keep it for yourself).

Input

The first line of the input contains two integers n and m (2 ≤ n ≤ 100 000, 1 ≤ m ≤ 100 000) — the number of vertices and the number of edges in the prize graph, respectively.

Each of the next m lines contains a pair of integers ui and vi (1  ≤  ui,  vi  ≤  n), denoting an undirected edge between ui and vi. It's guaranteed the graph won't contain any self-loops or multiple edges.

Output

If it's impossible to split the graph between Pari and Arya as they expect, print "-1" (without quotes).

If there are two disjoint sets of vertices, such that both sets are vertex cover, print their descriptions. Each description must contain two lines. The first line contains a single integer k denoting the number of vertices in that vertex cover, and the second line contains kintegers — the indices of vertices. Note that because of m ≥ 1, vertex cover cannot be empty.

Sample Input

Input

4 2
1 2
2 3

Output

1

2
1 3 

Input

3 3
1 2
2 3
1 3

Output

-1

题目大意就是把图分成二分图,如果不能的话,输出-1,如果能的话,分别输出2个部分的点的数量和点的标号。

代码:

#include<iostream>
#include<string.h>
using namespace std;
 
int list[100005];
int fenzu[100005];
 
int main()
{
	int n, m;
	cin >> n >> m;
	int fx, fy;
	memset(list, 0, sizeof(list));
	memset(fenzu, 0, sizeof(fenzu));
	int x, y;
	int zu = 1;
	while (m--)
	{
		cin >> x >> y;		
		if (list[x] == 0)
		{
			if (list[y] == 0)
			{
				fenzu[x] = zu;
				fenzu[y] = zu;
				list[x] = 1;
				list[y] = 2;
				zu++;
			}
			else
			{
				fenzu[x] = fenzu[y];
				list[x] = 3 - list[y];
			}
		}
		else
		{
			if (list[y] == 0)
			{
				fenzu[y] = fenzu[x];
				list[y] = 3 - list[x];
			}
			else
			{
				fx = fenzu[x];
				fy = fenzu[y];
				if (fx == fy)
				{
					if (list[x] == list[y])
					{
						cout << "-1";
						return 0;
					}
				}
				else
				{					
					if (list[x] == list[y])
					{
						for (int i = 1; i <= n; i++)
						if (fenzu[i] == fy)
						{
							fenzu[i] = fx;
							list[i] = 3 - list[i];
						}
					}					
					else for (int i = 1; i <= n; i++)
						if (fenzu[i] == fy)fenzu[i] = fx;
				}
			}
		}
	}
	int sum = 0;
	for (int i = 1; i <= n; i++)if (list[i] == 1)sum++;
	cout << sum << endl;
	for (int i = 1; i <= n; i++)if (list[i] == 1)cout << i<<" ";
	cout << endl << n - sum << endl;
	for (int i = 1; i <= n; i++)if (list[i] != 1)cout << i << " ";
	return 0;
}

可惜超时了,在test14超时了,test14估计是n和m非常大的了。

前面13个test都过了,想必这个代码是没有问题的,就是效率的问题。

很显然,主要的效率问题就是,在唯一的while循环里面,有1个从1到n的循环,只为了检索一些特定的目标出来。

所以很显然,我应该使用队列。

抛弃这个思想,用队列来做,其实很简单,只要记录每个点的所有邻居即可。

代码:

#include<iostream>
#include<vector>
#include<queue>
#include<string.h>
using namespace std;
 
vector<int>v[100005];
queue<int>q;
int list[100005];
 
int main()
{
	int n, m;
	cin >> n >> m;
	int x, y;
	while (m--)
	{
		cin >> x >> y;	
		v[x].insert(v[x].end(),y);
		v[y].insert(v[y].end(),x);
	}
	int k, key = 1;
	memset(list, 0, sizeof(list));
	vector< int >::iterator p;
	while (1)
	{
		while (list[key])key++;
		if (key > n)break;
		q.push(key);
		list[key] = 1;
		while (!q.empty())
		{
			k = q.front();
			q.pop();
			for (p = v[k].begin(); p != v[k].end(); p++)
			{
				if (list[*p] == list[k])
				{
					cout << "-1";
					return 0;
				}
				if (list[*p] == 0)
				{
					q.push(*p);
					list[*p] = 3 - list[k];
				}
			}
		}
	}
	int sum = 0;
	for (int i = 1; i <= n; i++)if (list[i] == 1)sum++;
	cout << sum << endl;
	for (int i = 1; i <= n; i++)if (list[i] == 1)cout << i<<" ";
	cout << endl << n - sum << endl;
	for (int i = 1; i <= n; i++)if (list[i] != 1)cout << i << " ";
	return 0;
}

表面上有3层循环,实际上外面的2层while循环是有限制的,合起来也就是大约n而已。

里面的循环就更不用说了,3层循环合起来也就是大约n而已。

所以这个算法还是很快的,果然AC了。

力扣 785. 判断二分图

存在一个 无向图 ,图中有 n 个节点。其中每个节点都有一个介于 0 到 n - 1 之间的唯一编号。给你一个二维数组 graph ,其中 graph[u] 是一个节点数组,由节点 u 的邻接节点组成。形式上,对于 graph[u] 中的每个 v ,都存在一条位于节点 u 和节点 v 之间的无向边。该无向图同时具有以下属性:

  • 不存在自环(graph[u] 不包含 u)。
  • 不存在平行边(graph[u] 不包含重复值)。
  • 如果 v 在 graph[u] 内,那么 u 也应该在 graph[v] 内(该图是无向图)
  • 这个图可能不是连通图,也就是说两个节点 u 和 v 之间可能不存在一条连通彼此的路径。

二分图 定义:如果能将一个图的节点集合分割成两个独立的子集 A 和 B ,并使图中的每一条边的两个节点一个来自 A 集合,一个来自 B 集合,就将这个图称为 二分图 。

如果图是二分图,返回 true ;否则,返回 false 。

示例 1:

输入:graph = [[1,2,3],[0,2],[0,1,3],[0,2]]
输出:false
解释:不能将节点分割成两个独立的子集,以使每条边都连通一个子集中的一个节点与另一个子集中的一个节点。

示例 2:

输入:graph = [[1,3],[0,2],[1,3],[0,2]]
输出:true
解释:可以将节点分成两组: {0, 2} 和 {1, 3} 。

提示:

  • graph.length == n
  • 1 <= n <= 100
  • 0 <= graph[u].length < n
  • 0 <= graph[u][i] <= n - 1
  • graph[u] 不会包含 u
  • graph[u] 的所有值 互不相同
  • 如果 graph[u] 包含 v,那么 graph[v] 也会包含 u

c++代码:

class Solution {
public:
	bool isBipartite(vector<vector<int>>& graph) {
		map<int, vector<int>>m;
		for (int i = 0; i < graph.size(); i++)m[i] = graph[i];
		UndirectedGraphData<int> g(m);
		return UndirectedGraph::isTwoDivGraph(m.size(), g);
	}
};

rust代码:

struct Union{
    fa:Vec<usize>,
    dif:Vec<i32>,
}
impl Union{
    fn find(& mut self,x:usize)->usize{
        if self.fa[x] == x{
            return x;
        }
        Union::find(self,self.fa[x]);
        self.dif[x] += self.dif[self.fa[x]];
        self.fa[x] = self.fa[self.fa[x]];
        return self.fa[x];
    }
    fn in_same(& mut self,x:usize,y:usize)->bool{
        return Union::find(self,x) == Union::find(self,y);
    }
    fn merge(& mut self,x:usize,y:usize,x_sub_y:i32)->(){
        Union::find(self,x);
        let fax = self.fa[x];
        self.dif[fax] = x_sub_y - self.dif[x];
        self.fa[fax] = y;
    }
}

impl Solution {
    pub fn is_bipartite(graph: Vec<Vec<i32>>) -> bool {
        let mut fa=Vec::new();
        let mut dif=Vec::new();
        for i in 0..graph.len(){
            fa.push(i);
            dif.push(0);
        }
        let mut opt = Union{fa:fa,dif:dif};
        for x in 0..graph.len(){
            for y in graph[x].clone(){
                let y = y as usize;
                if opt.in_same(x, y){
                    if (opt.dif[x]+opt.dif[y])%2 == 0{
                        return false;
                    }
                }else{
                    opt.merge(x, y, 1);
                }
            }
        }
        return true;
    }
}

二,完全二分图

1,完全二分图

完全二分图是一种特殊的二分图,可以把图中的顶点分成两个集合,使得第一个集合中的所有顶点都与第二个集合中的所有顶点相连。

2,K2,3

3,K3,3

三,匈牙利算法

1,二分图最大匹配

思路:

从0开始,逐步扩展。

扩展方法:寻找类似于0-1-2-3-4-5的路径,把匹配“1-2 3-4”调整成匹配“0-1 2-3 4-5”,匹配数加1。

示例:

调整前:

寻找路径:

调整后:

模板代码:

参考ACM模板

2,其他图论问题

待更新

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

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

相关文章

交易所上币:区块链项目上线交易所流程

一、了解交易所/申请上币 在区块链项目上线交易所之前,首先需要对交易所进行充分的了解,包括交易所的基本信息、交易规则、飞BTC5186上币标准等。还需要了解交易所的申请上币流程,以便为后续的操作做好准备。 1.1 选择合适的交易所 在众多的交易所中 飞(BTC5186),如何选择一个…

NineData云原生智能数据管理平台新功能发布|2024年3月版

数据库 DevOps - 大功能升级 SQL 开发早期主要提供 SQL 窗口&#xff08;IDE&#xff09;功能&#xff0c;在产品经过将近两年时间的打磨&#xff0c;新增了大量的企业级功能&#xff0c;已经服务了上万开发者&#xff0c;覆盖了数据库设计、开发、测试、变更等生命周期的功能…

EVM Layer2 主流解决方案

深度解析主流 EVM Layer 2 解决方案&#xff1a;zk Rollups 和 Optimistic Rollups 随着以太坊网络的不断演进和 DeFi 生态系统的迅速增长&#xff0c;以太坊 Layer 2 解决方案日益受到关注。 其中&#xff0c;zk Rollups 和 Optimistic Rollups 作为两种备受瞩目的主流 EVM&…

Springboot自动获取接口实现

ServiceLoader加载接口实现步骤 1.编写接口 public interface CommunicationAdapterFactory {void setKernel(LocalKernel kernel);boolean providesAdapterFor(Vehicle vehicle);BasicCommunicationAdapter getAdapterFor(Vehicle vehicle); }2.编写实现 // 实现类 1 publi…

Golang Gin框架

1、这篇文章我们简要讨论一些Gin框架 主要是给大家一个基本概念 1、Gin主要是分为路由和中间件部分。 Gin底层使用的是net/http的逻辑&#xff0c;net/http主要是说&#xff0c;当来一个网络请求时&#xff0c;go func开启另一个协程去处理后续(类似epoll)。 然后主协程持续…

利用Winform实现文字滚动(仅供参考)

本人水平有限&#xff0c;如有写得不对的地方&#xff0c;望指正。为了简单化&#xff0c;做了一个简陋版的滚动控件。本文的内容仅供参考 测试环境&#xff1a; visual studio 2017 .net framework 4.0 原理非常简单&#xff1a; 1 先自定义一个继承UserControl的控件&am…

item_search-按关键字搜索淘宝商品:如何通过获取以下关键字、搜索类型、排序方式参数提升用户体验、优化营销策略、提高转化率

在淘宝购物的过程中&#xff0c;搜索功能无疑是用户与商品之间的重要桥梁。通过输入关键字&#xff0c;用户可以迅速找到所需的商品&#xff0c;而搜索结果的准确性和相关性则直接关系到用户的购物体验和满意度。因此&#xff0c;如何通过优化关键字、搜索类型和排序方式参数&a…

element-ui tableData导出为xlsx文件

下载 npm i / yarn add file-saver、xlsx库 引入 import FileSaver from “file-saver”; import XLSX from “xlsx”; const simexport (data) > {// if (data.create_time && data.create_time.length > 0) {// data.start_time parseTime(data.create_tim…

Python实现【贪吃蛇大作战】+源码

文章目录 前言&#xff1a;一、游戏概述1.游戏玩法2.游戏特色 二、游戏规则三、工具选择四、主要技术pygame 库numpy 库cocos2d 五、源码分享六、项目地址 前言&#xff1a; 今天的GitHub小游戏分享&#xff0c;我们将聚焦于一个经典而又极富趣味性的游戏——贪吃蛇大作战。这…

Linux 桌面系统软件安装

我工作出来后一直是使用的 ubuntu 操作系统作为办公和娱乐。 主要的操作系统周边任务&#xff0c;代码编辑与编译环境&#xff0c;浏览网页&#xff0c; 文档处理&#xff0c; 多媒体操作这些没问题。 目前主要的矛盾点就是微信&#xff0c; 不过平时有手机能够处理&#xff0c…

线上线下陪玩,APP小程序H5。源码交付,支持二开!

线下陪玩的风险与管理方式 1、陪玩者的身心健康风险 线下陪玩的模式决定了陪玩者需要与不同的需求方见面&#xff0c;并满足他们的陪伴和娱乐需求。这种工作方式可能会给陪玩者带来身心上的压力和负担。因为陪玩者需要面对各种需求方的要求&#xff0c;有时还需要虚拟出一种完…

最新在线工具箱网站系统源码

最新在线工具箱网站系统源码 图片&#xff1a;最新在线工具箱网站系统源码 - 百创网-源码交易平台_网站源码_商城源码_小程序源码

LC 107.二叉树的层序遍历II

107. 二叉树的层序遍历 II 给你二叉树的根节点 root &#xff0c;返回其节点值 自底向上的层序遍历 。 &#xff08;即按从叶子节点所在层到根节点所在的层&#xff0c;逐层从左向右遍历&#xff09; 示例 1&#xff1a; 输入&#xff1a; root [3,9,20,null,null,15,7] 输出…

LeetCode_394(字符串解码)

双栈法 public String decodeString(String s) {String res "";Stack<Integer> countStack new Stack<>();Stack<String> resStack new Stack<>();int idx 0;while (idx < s.length()){char cur s.charAt(idx);//处理数字if(Charact…

2024最新软件测试【测试理论+ 抓包与网络协议】面试题(内附答案)

一、测试理论 3.1 你们原来项目的测试流程是怎么样的? 我们的测试流程主要有三个阶段&#xff1a;需求了解分析、测试准备、测试执行。 1、需求了解分析阶段 我们的 SE 会把需求文档给我们自己先去了解一到两天这样&#xff0c;之后我们会有一个需求澄清会议&#xff0c; …

消息存储与同步策略设计

消息存储与同步策略 https://github.com/robinfoxnan/BirdTalkServer 思路&#xff1a; 私聊写扩散&#xff0c;以用户为中心&#xff0c;存储2次&#xff1b;群聊读扩散&#xff0c;以群组为中心&#xff0c;存储一次&#xff1b;scylladb易于扩展&#xff0c;适合并发&…

idea2023.2.1 java项目-web项目创建-servlet类得创建

如何创建Java项目 1.1 方式1&#xff1a; 1.2 方式&#xff1a; 1.3 方式 如何创建web项目 方式 ----- 推荐 如何创建servlet类 复制6 中得代码 给servlet 配置一个路径 启动tomcat 成功了

AHKC系列霍尔电流传感器的选型及应用

安科瑞电气股份有限公司 祁洁 15000363176 一、霍尔传感器分类 01:开口式开环 02:闭口式开环 03:霍尔变送器 04:霍尔闭环 05:直流电压 06:直流漏电流 二、各类霍尔传感器 1、开口式开环霍尔 &#xff08;1&#xff09;精度1级 &#xff08;2&#xff09;成本低可…

物联网实战--入门篇之(四)嵌入式-UART驱动

目录 一、串口简介 二、串口驱动设计 三、串口发送 四、串口接收处理 五、PM2.5数据接收处理 六、printf重定义 七、总结 一、串口简介 串口在单片机的开发中属于非常常用的外设&#xff0c;最基本的都会预留一个调试串口用来输出调试信息&#xff0c;串口时序这里就不谈…

视频监控/云存储/磁盘阵列/AI智能分析平台EasyCVR集成时调用接口报跨域错误是什么原因?

EasyCVR视频融合平台基于云边端架构&#xff0c;可支持海量视频汇聚管理&#xff0c;能提供视频监控直播、云端录像、云存储、录像检索与回看、智能告警、平台级联、智能分析等视频服务。平台兼容性强&#xff0c;支持多协议、多类型设备接入&#xff0c;包括&#xff1a;国标G…