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;

      // 遍历女生a
      for (int a = 1; a <= m; a++) {
        // 从女生a开始进行增广路探索,探索前,需要将vis重置
        Arrays.fill(vis, false);
        // 如果a找到配对男生,则配对边+1
        if (dfs(a)) ans++;
      }

      System.out.println(ans);
    }
  }

  public static boolean dfs(int a) {
    // 遍历男生b
    for (int b = 1; b <= n; b++) {
      // 如果男生b不在女生a发起的探索的增广路中,且a,b可以配对
      if (!vis[b] && edges[a][b]) {
        // 则当前增广路加入男生b
        vis[b] = true;

        // 如果男生b没有被其他人配对 || 已经和其他人配对,但是男生b当前配对的女生match[b]可以放弃男生b,而和其他男生配对
        if (match[b] == 0 || dfs(match[b])) {
          // 则男生b可以和女生a配对,即配对成功,match[b] = a
          match[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;

  // 遍历女生a
  for (let a = 1; a <= m; a++) {
    // 从女生a开始进行增广路探索,探索前,需要将vis重置
    vis.fill(false);
    // 如果a找到配对男生,则配对边+1
    if (dfs(a)) ans++;
  }

  function dfs(a) {
    // 遍历男生b
    for (let b = 1; b <= n; b++) {
      // 如果男生b不在女生a发起的探索的增广路中,且a,b可以配对
      if (!vis[b] && edges[a][b]) {
        // 则当前增广路加入男生b
        vis[b] = true;

        // 如果男生b没有被其他人配对 || 已经和其他人配对,但是男生b当前配对的女生match[b]可以放弃男生b,而和其他男生配对
        if (match[b] == 0 || dfs(match[b])) {
          // 则男生b可以和女生a配对,即配对成功,match[b] = a
          match[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):
    #  遍历男生b
    for b in range(1, n + 1):
        # 如果男生b不在女生a发起的探索的增广路中,且a,b可以配对
        if not vis[b] and edges[a][b]:
            # 则当前增广路加入男生b
            vis[b] = True

            # 如果男生b没有被其他人配对 || 已经和其他人配对,但是男生b当前配对的女生match[b]可以放弃男生b,而和其他男生配对
            if match[b] == 0 or dfs(match[b], vis):
                # 则男生b可以和女生a配对,即配对成功,match[b] = a
                match[b] = a
                return True

    return False


# 算法入口
def getResult():
    ans = 0

    for a in range(1, m + 1):
        # vis[b] 表示男生b是否在a女生发起的增广路探索中
        vis = [False] * (n + 1)

        # 如果a找到配对男生,则配对边+1
        if dfs(a, vis):
            ans += 1

    return ans


# 算法调用
print(getResult())

C算法源码

#include <stdio.h>

#define MAX_SIZE 501

#define TRUE 1
#define FALSE 0

int 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) {
    // 遍历男生b
    for (int b = 1; b <= n; b++) {
        // 如果男生b不在女生a发起的探索的增广路中,且a,b可以配对
        if (!vis[b] && edges[a][b]) {
            // 则当前增广路加入男生b
            vis[b] = 1;

            // 如果男生b没有被其他人配对 || 已经和其他人配对,但是男生b当前配对的女生match[b]可以放弃男生b,而和其他男生配对
            if (match[b] == 0 || dfs(match[b])) {
                // 则男生b可以和女生a配对,即配对成功,match[b] = a
                match[b] = a;
                return TRUE;
            }
        }
    }

    return FALSE;
}

int main() {
    while (1) {
        int k;
        scanf("%d", &k);

        if (k == 0) break;

        scanf("%d %d", &m, &n);

        // 初始化edges
        for (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;
        }

        // 初始化match
        for (int i = 0; i <= n; i++) {
            match[i] = 0;
        }

        // ans记录题解
        int ans = 0;
        // 遍历女生
        for (int a = 1; a <= m; a++) {
            // 初始化vis
            for (int i = 0; i <= n; i++) {
                vis[i] = FALSE;
            }

            // 如果女生a找到匹配男生,则匹配边++
            if (dfs(a)) {
                ans++;
            }
        }

        printf("%d\n", ans);
    }

    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用STM32微控制器驱动LCD1602显示器

驱动LCD1602显示器是嵌入式系统常见的任务之一&#xff0c;而STM32微控制器因其灵活性和丰富的外设而成为了广泛采用的解决方案。在这篇文章中&#xff0c;我们将探讨如何使用STM32微控制器来驱动LCD1602显示器。我们将从STM32的GPIO配置、延时函数以及LCD1602的初始化和写入数…

【数值分析】非线性方程求根,牛顿法,牛顿下山法,matlab实现

4. 牛顿法 收敛时牛顿法的收敛速度是二阶的&#xff0c;不低于二阶。如果函数有重根&#xff0c;牛顿法一般不是二阶收敛的。 x k 1 x k − f ( x k ) f ′ ( x k ) x_{k1}x_k- \frac{f(x_k)}{f(x_k)} xk1​xk​−f′(xk​)f(xk​)​ matlab实现 %% 牛顿迭代例子 f (x) x…

【信息论与编码】习题-填空题

目录 填空题1.克劳夫特不等式是判断&#xff08; &#xff09;的充要条件。2.无失真信源编码的中心任务是编码后的信息率压缩接近到&#xff08;&#xff09;限失真压缩中心任务是在给定的失真度条件下&#xff0c;信息率压缩接近到&#xff08; &#xff09;。3.常用的检纠错方…

使用 KubeSphere 与极狐GitLab 打造云原生持续交付系统

极狐GitLab 简介 极狐GitLab 是一个一体化的 DevOps 平台&#xff0c;可以简单理解为 GitLab 在国内的“发行版”。是由极狐(GitLab)公司推出的产品&#xff08;极狐(GitLab)公司是以“中外合资3.0”模式成立的公司&#xff0c;在国内独立运营&#xff0c;为国内用户提供适合本…

CSS 实现两个圆圈重叠部分颜色不同

这是期望实现的效果&#xff0c;由图可知&#xff0c;圆圈底图透明度是0.4&#xff0c;左侧要求重叠部分透明度是0.7&#xff0c;所以不能通过简单的透明度叠加来实现最右侧的效果。 这就需要另外新建一个图层来叠加在两个圆圈重叠上方。 直接看代码 .circle_hight {width: 1…

模版匹配历劫之路1-匹配点太多如何解决

1测试图片 2初步推测是否是提取的点太多而导致匹配时间很长 2.1通过canny的算法来提取检测点 import numpy as np import cv2 import time import matplotlib.pyplot as pltclass GeoMatch:def __init__(self):self.noOfCordinates0 # 坐标数组中元素的个数self.cordinates…

2024年某书最新x-s-common签名算法分析以及点赞api接口测试nodejs(2024-01-05)

2024年某书又更新了x-s-common算法&#xff0c;现在的版本是&#xff1a;3.6.8。这个签名算法现在是越来越重要了&#xff0c;许多接口都要用到。比如&#xff1a;评论&#xff0c;点赞等接口&#xff0c;没有这个算法采集不到数据。 一、chrome逆向x-s-common算法 1、x-s-comm…

开启Android学习之旅-3-Android Activity

Android Activity 本文总结《第一行代码 Android》第3版的内容 环境&#xff1a; Android Studio Giraffe | 2022.3.1 Patch 3 Activity 是什么&#xff1f; Activity 简单将就是UI界面&#xff0c;包含两部分 Activity 类 和应用布局文件&#xff0c;如果是 Compose 则另说&…

1.4 SPEEDING UP REAL APPLICATIONS

我们从并行化应用程序中可以期待什么样的速度&#xff0c;这取决于应用程序中可以并行化的部分。如果可并行化部分所花费时间的百分比为30%&#xff0c;则并行部分的100倍加速将使执行时间减少不超过29.7%。整个应用程序的加速速度将仅为1.4倍左右。事实上&#xff0c;即使在并…

云渲染电脑可以关吗?瑞云渲染能断开网络吗?

随着技术的发展&#xff0c;网络速度从4G到5G的提升&#xff0c;也给云渲染行业对于渲染文件的传输上变的更加快速。且随着云服务商对于硬件的升级&#xff0c;高性能的渲染配置越发强大。云渲染在渲染速度上变得更加的快速。对于刚刚接触云渲染的新手小白有着不少的疑问。下面…

硬盘检测软件 SMART Utility mac功能特色

SMART Utility for mac是一款苹果电脑上磁盘诊断工具&#xff0c;能够自动检测磁盘的状态和错误情况&#xff0c;分析并提供错误报告,以直观的界面让用户可明确地知道自己的磁盘状况。SMART Utility 支持普通硬盘HDD和固态硬盘SSD&#xff0c;能够显示出详细的磁盘信息&#xf…

Qt6入门教程 2:Qt6下载与安装

Qt6不提供离线安装包&#xff0c;下载和安装实际上是一体的了。 关于Qt简介&#xff0c;详见&#xff1a;Qt6入门教程1&#xff1a;Qt简介 一.下载在线安装器 Qt官网 地址&#xff1a;https://download.qt.io/ 在线下载器地址&#xff1a;https://download.qt.io/archive/on…

RMAN-03002 RMAN-06059 ORA-19625

有个现场经理反馈&#xff0c;每天的rman备份异常&#xff0c;登录系统查看rman的log日志&#xff0c;报错信息如下 RMAN> run{ 2> backup filesperset 50 archivelog all format /backup/ARCHBAK_%d_%T_%s tag arch_bak delete all input; 3> } 4> Starting …

java: 写入数据到HBase

一、添加依赖 <dependency><groupId>org.apache.hadoop</groupId><artifactId>hadoop-client</artifactId><version>2.6.0</version></dependency><dependency><groupId>org.apache.hbase</groupId><art…

flutter版本升级后,解决真机和模拟器运行错误问题

flutter从3.3.2升级到3.16.0&#xff0c;项目运行到真机和模拟器报同样的错&#xff0c;错误如下: 解决办法&#xff1a;在android目录下的build.gradle加入下面这行&#xff0c;如下图&#xff1a; 重新运行&#xff0c;正常把apk安装到真机上或者运行到模拟器上

.NET Standard 支持的 .NET Framework 和 .NET Core

.NET Standard 是针对多个 .NET 实现推出的一套正式的 .NET API 规范。 推出 .NET Standard 的背后动机是要提高 .NET 生态系统中的一致性。 .NET 5 及更高版本采用不同的方法来建立一致性&#xff0c;这种方法在大多数情况下都不需要 .NET Standard。 但如果要在 .NET Framewo…

密码学(一)

文章目录 前言一、Cryptographic Primitives二、Cryptographic Keys2.1 Symmetric key cryptography2.2 asymmetric key cryptography 三、Confidentiality3.1 Symmetric key encryption algorithms3.2 asymmetric key block ciphers3.3 其他 四、Integrity4.1 symmetric key s…

亲测表白网制作源码,在线制作表白,无数据库上传就能用

在线制作表白网源码 没有数据库上传就能用 后台/admin 账号密码都是admin

Android低功耗蓝牙开发总结

基础使用 权限申请 蓝牙权限在各个版本中略有不同 Android 12 及以上版本&#xff0c;如果不需要通过蓝牙来推断位置的话&#xff0c;蓝牙扫描不需要开启位置权Android 11 及以下版本&#xff0c;蓝牙扫描必须开启位置权限Android 9 及以下版本&#xff0c;蓝牙扫描可开启粗…

Windows BAT脚本 | 定时关机程序

使用说明&#xff1a;输入数字&#xff0c;实现一定时间后自动关机。 单位小时&#xff0c;用后缀 h 或 H。示例 1h 单位分钟&#xff0c;用后缀 m 或 M 或 min。示例 30min 单位秒。用后缀 s 或不用后缀。示例 100s 源码 及 配置方法 桌面新建文本文件&#xff0c;输入下面…