HDU - 2063 过山车(Java JS Python C)

题目来源

Problem - 2063 (hdu.edu.cn)

题目描述

RPG girls今天和大家一起去游乐场玩,终于可以坐上梦寐以求的过山车了。

可是,过山车的每一排只有两个座位,而且还有条不成文的规矩,就是每个女生必须找个男生做partner和她同坐。

但是,每个女孩都有各自的想法,举个例子把,

  • Rabbit只愿意和XHD或PQK做partner
  • Grass只愿意和linle或LL做partner
  • PrincessSnow愿意和水域浪子或伪酷儿做partner

考虑到经费问题,boss刘决定只让找到partner的人去坐过山车,其他的人,嘿嘿,就站在下面看着吧。

聪明的Acmer,你可以帮忙算算最多有多少对组合可以坐上过山车吗?

输入描述

输入数据的第一行是三个整数K , M , N,分别表示可能的组合数目,女生的人数,男生的人数。

  • 0 < K ≤ 1000
  • 1 ≤ N, M ≤ 500

接下来的K行,每行有两个数,分别表示女生 Ai 愿意和男生 Bj 做partner。

最后一个0结束输入。

输出描述

对于每组数据,输出一个整数,表示可以坐上过山车的最多组合数。

用例

输入6 3 3
1 1
1 2
1 3
2 1
2 3
3 1
0
输出3

题目解析

本题是二分图最大匹配问题。

首先,我们需要知道什么是二分图?

二分图是一种特殊的图结构,二分图中的节点可以分为两部分,每个部分中的节点之间互不相连,比如下图所示:

绿色点集是一个部分,橙色点集是一个部分,各个部分中节点之间互不相连

二分图的 "匹配" 指的是 "边的集合“,"匹配"中的各个边之间没有共同节点,即每个边都是独立的。

比如下图中的两个红色边就形成了一个匹配,因为1-4边,2-5边之间没有共同节点,两个边是互相独立的。

二分图的最大匹配,指的是,边最多的"匹配",比如下图中,1-5边,2-6边,3-4边之间互相独立,可以形成一个数量为3条边的匹配。下面二分图最多只能有3条边的匹配。

那么,如何求二分图的最大匹配呢?

最常用的办法就是匈牙利算法。

在解释匈牙利算法之前,我们需要先对二分图最大匹配问题的实际意义做一个解释。

比如现在有男生若干,女生若干,而每个男生都有心仪的多个女生,每个女生也有心仪的多个男生,而男生、女生只会和心仪的对象进行配对,现在需要实现最大配对?

上面例子就是二分图最大匹配的典型应用。

我们假设节点1,2,3是男生,4,5,6是女生,那么初始时,存在如下心仪关系:

  • 1 和 4 互相心仪
  • 1 和 5 互相心仪
  • 2 和 5 互相心仪
  • 2 和 6 互相心仪
  • 3 和 4 互相心仪

因此可得二分图如下:

下面开始演示匈牙利算法来找到最大匹配数:

我们需要选择二分图的一部分作为发起配对请求的一方,比如我们选择男生作为发起配对请求的一方,此时遍历每一个男生:

假设先遍历出男生1,而男生1和与女生4,女生5互相心仪,此时我们可以任选一个女生进行配对,比如选择女生4进行配对,由于女生4当前没有配对对象,因此男生1和女生4可以配对成功

男生1完成配对后,继续遍历下一个男生2发起配对请求,而男生2和女生5,女生6互相心仪,此时我们可以任选一个女生进行配对,比如选择女生5进行配对,由于女生5当前没有配对对象,因此男生2和女生5可以配对成功

男生2完成配对后,继续遍历下一个男生3发起配对请求,而男生3只和女生4互相心仪,但是女生4已经和男生1配对了!!!

此时,为了实现最大配对,我们只能委屈男生1,让他找一找其他可以配对的女生,然后发现男生1还和女生5互相心仪,因此我们尝试将男生1和女生5配对。

但是女生5已经和男生2发生配对了!!!

因此,为了实现最大配对,只能委屈男生2找找看其他可以配对的女生,然后发现男生2还和女生6互相心仪,因此尝试将那是2和女生6进行配对

而女生6没有发生过配对,因此男生2可以和女生6成功配对。

由于男生2和女生6成功配对了,因此虚线2-6变为实线,而一个男生只能和一个女生配对,因此实线2-5变为虚线

而由于女生5和男生2解除了配对状态,因此男生1和女生5就可以成功配对,因此1-5从虚线变为实线,而一个男生只能和一个女生配对,因此实线1-4变为虚线

由于女生4和男生1解除了配对状态,因此男生3和女生4可以成功配对,因此虚线3-4变为实线

此时我们完成了所有男生的配对,即得到了最大匹配。

上面,从配对情况图中,黑虚线和红实线,逆变为,黑实线和红虚线,实现了配对数+1,

如下图左边只有两个配对(红色线),右边有三个配对(黑色线)

我们将左边这种展开为“虚实虚实虚”的情况,称为增广路,增广路的特点是两端为虚,中间虚实交替,增广路的逆变,可以实现配对数+1。

关于增广路的定义和意义就是如此,了解即可。

以上就是匈牙利算法的实现过程。总结一下就是:

初始时,寻找一方作为配对发起方,比如男生,遍历每一个男生发起配对请求:

男生a只能对互相心仪的女生发起配对请求,

  • 如果互相心仪的女生b没有发生过配对,则和当前男生配对成功
  • 如果互相心仪的女生b已经发生了配对,那么需要找到女生b的配对对象男生c,并尝试让男生c重新找一个可以配对的其他女生(此时为递归重复逻辑,即男生c对其他互相心仪的女生发起配对请求),如果最终男生c可以配对其他女生,则男生a与女生b配对成功,否则男生a与女生b无法配对。

按照上面逻辑我们让每一个男生都发起配对请求,最终我们可以找到最大匹配数。

更多细节请看下面代码实现。

Java算法源码

import java.util.Arrays;
import java.util.Scanner;public class Main {static int m;static int n;static boolean[][] edges;static int[] match;static boolean[] vis;public static void main(String[] args) {Scanner sc = new Scanner(System.in);while (sc.hasNextInt()) {int k = sc.nextInt();if (k == 0) break;m = sc.nextInt(); // m个女生n = sc.nextInt(); // n个男生// edges[a][b] == true 表示 a女生 和 b男生 可以配对edges = new boolean[m + 1][n + 1];for (int i = 0; i < k; i++) {int a = sc.nextInt();int b = sc.nextInt();edges[a][b] = true;}// match[b] 表示男生b配对的女生match = new int[n + 1];// vis[b] 表示男生b是否在本次增广路中vis = new boolean[n + 1];// ans记录题解int ans = 0;// 遍历女生afor (int a = 1; a <= m; a++) {// 从女生a开始进行增广路探索,探索前,需要将vis重置Arrays.fill(vis, false);// 如果a找到配对男生,则配对边+1if (dfs(a)) ans++;}System.out.println(ans);}}public static boolean dfs(int a) {// 遍历男生bfor (int b = 1; b <= n; b++) {// 如果男生b不在女生a发起的探索的增广路中,且a,b可以配对if (!vis[b] && edges[a][b]) {// 则当前增广路加入男生bvis[b] = true;// 如果男生b没有被其他人配对 || 已经和其他人配对,但是男生b当前配对的女生match[b]可以放弃男生b,而和其他男生配对if (match[b] == 0 || dfs(match[b])) {// 则男生b可以和女生a配对,即配对成功,match[b] = amatch[b] = a;return true;}}}return false;}
}

 

JS算法源码

const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;void (async function () {// m个女生, n个男生, k条边const [k, m, n] = (await readline()).split(" ").map(Number);// edges[a][b] == true 表示 a女生 和 b男生 可以配对const edges = new Array(m + 1).fill(0).map(() => new Array(n + 1).fill(false));for (let i = 0; i < k; i++) {const [a, b] = (await readline()).split(" ").map(Number);edges[a][b] = true;}await readline(); // 读取最后一行输入的0// match[b] 表示男生b配对的女生const match = new Array(n + 1).fill(0);// vis[b] 表示男生b是否在本次增广路中const vis = new Array(n + 1).fill(false);// ans记录题解let ans = 0;// 遍历女生afor (let a = 1; a <= m; a++) {// 从女生a开始进行增广路探索,探索前,需要将vis重置vis.fill(false);// 如果a找到配对男生,则配对边+1if (dfs(a)) ans++;}function dfs(a) {// 遍历男生bfor (let b = 1; b <= n; b++) {// 如果男生b不在女生a发起的探索的增广路中,且a,b可以配对if (!vis[b] && edges[a][b]) {// 则当前增广路加入男生bvis[b] = true;// 如果男生b没有被其他人配对 || 已经和其他人配对,但是男生b当前配对的女生match[b]可以放弃男生b,而和其他男生配对if (match[b] == 0 || dfs(match[b])) {// 则男生b可以和女生a配对,即配对成功,match[b] = amatch[b] = a;return true;}}}return false;}console.log(ans);
})();

 

Python算法源码

# 输入获取
k, m, n = map(int, input().split())  # k个配对, m个女生, n个男生edges = [[False] * (n + 1) for _ in range(m + 1)]
for _ in range(k):a, b = map(int, input().split())edges[a][b] = True  # edges[a][b] == true 表示 a女生 和 b男生 可以配对input()match = [0] * (n + 1)  # match[b] 表示男生b确定配对的女生def dfs(a, vis):#  遍历男生bfor b in range(1, n + 1):# 如果男生b不在女生a发起的探索的增广路中,且a,b可以配对if not vis[b] and edges[a][b]:# 则当前增广路加入男生bvis[b] = True# 如果男生b没有被其他人配对 || 已经和其他人配对,但是男生b当前配对的女生match[b]可以放弃男生b,而和其他男生配对if match[b] == 0 or dfs(match[b], vis):# 则男生b可以和女生a配对,即配对成功,match[b] = amatch[b] = areturn Truereturn False# 算法入口
def getResult():ans = 0for a in range(1, m + 1):# vis[b] 表示男生b是否在a女生发起的增广路探索中vis = [False] * (n + 1)# 如果a找到配对男生,则配对边+1if dfs(a, vis):ans += 1return ans# 算法调用
print(getResult())

C算法源码

#include <stdio.h>#define MAX_SIZE 501#define TRUE 1
#define FALSE 0int m, n; // m个女生, n个男生
int edges[MAX_SIZE][MAX_SIZE]; // edges[a][b] == true 表示 a女生 和 b男生 可以配对
int match[MAX_SIZE]; // match[b] 表示男生b配对的女生
int vis[MAX_SIZE]; // vis[b] 表示男生b是否在本次增广路中int dfs(int a) {// 遍历男生bfor (int b = 1; b <= n; b++) {// 如果男生b不在女生a发起的探索的增广路中,且a,b可以配对if (!vis[b] && edges[a][b]) {// 则当前增广路加入男生bvis[b] = 1;// 如果男生b没有被其他人配对 || 已经和其他人配对,但是男生b当前配对的女生match[b]可以放弃男生b,而和其他男生配对if (match[b] == 0 || dfs(match[b])) {// 则男生b可以和女生a配对,即配对成功,match[b] = amatch[b] = a;return TRUE;}}}return FALSE;
}int main() {while (1) {int k;scanf("%d", &k);if (k == 0) break;scanf("%d %d", &m, &n);// 初始化edgesfor (int i = 0; i <= m; i++) {for (int j = 0; j <= n; j++) {edges[i][j] = FALSE;}}// 关联可匹配的男女生for (int i = 0; i < k; i++) {int a, b;scanf("%d %d", &a, &b);edges[a][b] = TRUE;}// 初始化matchfor (int i = 0; i <= n; i++) {match[i] = 0;}// ans记录题解int ans = 0;// 遍历女生for (int a = 1; a <= m; a++) {// 初始化visfor (int i = 0; i <= n; i++) {vis[i] = FALSE;}// 如果女生a找到匹配男生,则匹配边++if (dfs(a)) {ans++;}}printf("%d\n", ans);}return 0;
}

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

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

相关文章

GPT3.5 改用 GPT4 价格翻了30倍 如何破局? GPT 对话成本推演

场景介绍 假设你搭建了一个平台&#xff0c;提供 ChatGPT 3.5 的聊天服务。目前已经有一批用户的使用数据&#xff0c;想要测算一下如果更换 GPT 4.0 服务需要多少成本&#xff1f; 方案阐述 如果是全切&#xff0c;最简单粗暴的方案就是根据提供 ChatGPT 3.5 消费的金额乘…

数据结构:STL:queue stack

目录 1.queue的头文件 2.queue的定义 3.queue的常用函数 3.1 push() 3.2 pop() 3.3 size() 3.4 empty() 3.5 front() 3.6 back() 4.stack的头文件 5.stack的定义 6.stack的常用函数 6.1 push() 6.2 top() 6.3 pop() 6.4 size() 6.6 empty() STLf封装的queue也是…

集合基础知识点

集合基础 1. 集合的由来 当 Java 程序中需要存放数据的时候&#xff0c;通常会定义变量来实现数据的存储&#xff0c;但是&#xff0c;当需要存储大量数据的时候该怎么办呢&#xff1f;这时首先想到的是数组&#xff0c;但是&#xff01;数组只能存放同一类型的数据&#xff…

16 Linux 内核定时器

一、Linux 时间管理和内核定时器简介 1. 内核时间管理简介 Linux 内核中有大量的函数需要时间管理&#xff0c;比如周期性的调度程序、延时程序、定时器等。 硬件定时器提供时钟源&#xff0c;时钟源的频率可以设置&#xff0c;设置好以后就周期性的产生定时中断&#xff0c;系…

连续学习(Continual Learning)或者增量学习的场景中,multiband和replay分别是什么?起到什么作用

multiband和replay是两种不同的训练策略&#xff0c;通常用在处理连续学习或者增量学习的场景中。这些策略旨在解决新知识学习导致旧知识遗忘的问题&#xff0c;即所谓的灾难性遗忘。以下是multiband和replay两种策略的基本区别&#xff1a; Multiband: 定义: multiband通常是…

C语言编译器(C语言编程软件)完全攻略(第十六部分:Dev C++下载地址和安装教程(图解))

介绍常用C语言编译器的安装、配置和使用。 十六、Dev C下载地址和安装教程&#xff08;图解&#xff09; Dev C是一款免费开源的 C/C IDE&#xff0c;内嵌GCC编译器&#xff08;GCC 编译器的 Windows 移植版&#xff09;&#xff0c;是 NOI、NOIP 等比赛的指定工具。Dev C 的…

Spring中的数据校验

文章目录 引言摘要正文基于 ValidationUtils的简单校验基于自定义 Validator的校验Spring内置校验 LocalValidatorFactoryBeanHibernateValidator校验使用HibernateValidator自定义校验规则 总结 引言 我们在日常的软件开发过程中&#xff0c;尤其是WEB开发过程中&#xff0c;…

读算法霸权笔记12_数据科学

1. 公平与公正 1.1. 公平大多数时候只是副产品 1.2. 由贪婪或偏见导致的不公正一直发生在我们身边 1.2.1. 如果承认法律面前人人平等&#xff0c;或者作为选民的大众应该被平等对待&#xff0c;我们就不能允许模型把我们分为不同的群体进行区别对待 1.3. 对于数学模型来说&…

YOLOv5:指定类别进行评估验证

YOLOv5&#xff1a;指定类别进行评估验证 前言前提条件相关介绍实验环境YOLOv5&#xff1a;指定类别进行评估验证代码实现进行验证没有指定的结果指定类别的结果 前言 由于本人水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评改正。更多精彩内容&#xff0c;可点击进入P…

STM32学习笔记二十:WS2812制作像素游戏屏-飞行射击游戏(10)探索游戏平衡

游戏平衡很重要&#xff0c;然而&#xff0c;却往往得不到开发者的重视。或者&#xff0c;没有花时间仔细去做调整。 做过游戏开发的&#xff0c;都听说过一个词叫“数值爆炸”&#xff0c;实际上就是平衡没做好。 怎么样才能算是平衡呢&#xff1f; 玩家投入游戏的有两个&a…

农业银行RPA实践 3大典型案例分析

零接触开放金融服务在疫情之下被越来越多的银行和客户所认同&#xff0c;引起了更广泛的持续关注&#xff0c;各家银行纷纷开展产品服务创新&#xff0c;加速渠道迁移&#xff0c;同时通过远程办公、构建金融生态等方式积极推进零接触开放金融体系建设。 随着商业银行科技力量的…

Ansible的安装及简单使用

## Ansible的安装及简单使用 ## 一.Ubuntu安装Ansible sudo apt update sudo apt install ansible #使用以下命令检查安装是否成功&#xff1a; ansible --version二.配置Ansible #进入配置文件目录 cd /etc/ansible/ ls#文件含义 ansible.cfg #ansible配置文件,默认基本不用…

scratch绘制小正方形 2023年12月中国电子学会图形化编程 少儿编程 scratch编程等级考试四级真题和答案解析

目录 scratch绘制小正方形 一、题目要求 1、准备工作 2、功能实现 二、案例分析

紫光展锐5G扬帆出海 | 欧洲积极拥抱更多5G选择

和我国一样&#xff0c;欧洲不少国家也在2019年进入5G商用元年&#xff1a;英国在2019年5月推出了5G商用服务&#xff0c;该国最大的移动运营商EE(Everything Everywhere)最先商用5G&#xff1b;德国在2019年年中推出5G商用服务&#xff0c;德国电信、沃达丰和 Telefonica是首批…

Hive实战:分科汇总求月考平均分

文章目录 一、实战概述二、提出任务三、完成任务&#xff08;一&#xff09;准备数据1、在虚拟机上创建文本文件2、上传文件到HDFS指定目录 &#xff08;二&#xff09;实现步骤1、启动Hive Metastore服务2、启动Hive客户端3、创建分区的学生成绩表4、按分区加载数据5、查看分区…

nginx rewrite重写URL地址, laravel路由404问题

前言 在开发项目时&#xff0c;我面临一个需求&#xff1a;区分移动端和桌面端访问路径。移动端访问应在路径前加上/m/&#xff0c;而桌面端则不需要。例如&#xff1a; 移动端: 域名/m/路由地址桌面端: 域名/路由地址 这种设计在路由规则上带来了一定的重复&#xff0c;因为…

[C#]使用OpenCvSharp实现二维码图像增强超分辨率

【官方框架地址】 github.com/shimat/opencvsharp 【算法介绍】 借助于opencv自带sr.prototxt和sr.caffemodel实现对二维码图像增强 【效果展示】 【实现部分代码】 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin…

【机器学习】循环神经网络(一)

一、网络结构 RNN 处理输入序列时的信息流。 粗体箭头为各时间点信息流的活跃路径&#xff0c;虚线箭头显示当时不活动的连接。 一个简单RNN例子 RNN 不是一类网络&#xff0c;而是适用于不同问题的拓扑结构的集合。循环网络的一个有趣的方面是&#xff0c;有了足够的层和节点&…

export命令详解

export命令详解 大家好&#xff0c;我是免费搭建查券返利机器人赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天&#xff0c;让我们一同深入了解 Linux 和 Unix 系统中的一个强大命令——export&#xff0c;并…

PC+Wap仿土巴兔装修报价器源码 PHP源码

核心功能&#xff1a; 业主自助预算计算&#xff1a;通过简洁的界面&#xff0c;业主可以输入装修需求&#xff0c;系统自动进行预算计算信息自动收集&#xff1a;系统自动收集业主的基本信息&#xff0c;如姓名、联系方式、房屋面积等一键发送报价&#xff1a;业主完成预算计…