海王算法(看完不会变成海王)

简介: 海王算法(看完不会变成海王)

🌊海王算法的概念

💧海王算法又叫匈 牙 利 算 法 \color{#00BFFF}{匈牙利算法}匈牙利算法是由匈牙利数学家Edmonds于1965年提出,因而得名。匈牙利算法是基于Hall定理中充分性证明的思想,它是部 图 匹 配 \color{#00BFFF}{部图匹配}部图匹配最常见的算法,该算法的核心就是寻 找 增 广 路 径 \color{#00BFFF}{寻找增广路径}寻找增广路径,它是一种用增广路径求 二 分 图 最 大 匹 配 \color{#00BFFF}{求二分图最大匹配}求二分图最大匹配的算法。


前景提要

   🍊我个人很喜欢章若楠和许光汉 两位明星,那么,今天我就要来当个媒婆,干啥事儿呢?我要撮合他们,尽量让他们在一起。

   🍊由于我只提到了上面两位明星,那么我就用他们不同的照片来分别作为男一号、男二号、男三号、男四号,女一号、女二号、女三号、女四号,可能有点不太好,但是,我目前觉得还挺好,嘿嘿,就这样吧!


   🍊今天,我是一名伟大的媒婆!我要把男主们和女主们尽最大努力撮合在一起!既然是两个人在一起,那还是得先遵循他们自己的意愿吧。下图中的连线就表示两人相互有好感,也就是说可以尝试在一起一下,我们用个小本本记录一下,看看有哪些连线。(海王算法嘛,当然有海王咯,海王们一般都不专一,一次性喜欢多个人虽然很下头,但还是比较合理的。狗头保命 )


具体做法

  🐲 这个时候,天色骤变,海面上出现了一个巨 大 的 怪 兽 \color{#00BFFF}{巨大的怪兽}巨大的怪兽🦕🌊🌊🌊,它咆哮着说:“我就是万恶的海 王 \color{#00BFFF}{海王}海王(呜~~~),今天这桩事必须听我的,让我来给他们分配对象,否则我就…”。


   🏄‍♂️好吧那我们就听它的,看看它能玩出什么花样。


   🐲海王说:“第一步:我先试着给男一号找对象。”


   🏄‍♂️我给海王说:“我这里有个小本子,上面有他们彼此之间的暧昧关系💗。”


   🐲海王一看:“好家伙,到底谁是海王? 这样吧,男一号和女一号都还没确定关系,那我们就先给他们俩搓一对儿吧”。就这样,男一号和女一号暂时牵手成功了💘。(这里用粉紫色的双向箭头替换原来的连线,表示他们双向奔赴,在一起了。)


   🏄‍♂️紧接着,海王发现男二号和女二号也都没有对象,于是给他们俩也安排上了。男二女二暂时牵手成功💘。

 

🏄‍♂️接下来是男三号,海王发现与男二号有好感的女一号和女二号都已经名花有主了。这可咋办呢?


   🐲海王发话了:“男三号喜欢女一号是吧,那就先把女一号和男一号拆开,重新分配一下。”(用灰色的先表示分手。)


   🏄‍♂️绝了,什么操作啊?人家手还没牵热乎呢!真不愧是海王,什么事都做得出来!”


   🐲海王:“你少废话,信不信我把你…”

  🏄‍♂️于是我眼睁睁看着男一号和女一号分开了。男三号和女一号在一起了。那男一号咋办呢?

 🐲海王:“男一号不是还喜欢女二号吗,让他们俩试试。”

   🏄‍♂️于是男二号和女二号分开了,男一号和女二号在一起了。那男二号咋办呢?

🐲海王:“男二号不是还喜欢女三号吗,让他们俩试试。”

🏄‍♂️于是男二号和女三号在一起了。诶嘿,你别说,你还真别说,虽然海王这样的做法很下头,但是确实多撮合了一对儿出来,还算有点东西。那我们的男四号呢?


   🐲海王看了看小本子说:“这最后一个男生,我按照刚才的转移大法,也不能给他腾个女生出来,毕竟如果我满足了他,那前面的男生总会有一个分手,既然不能多撮合一对儿,那我就省省功力了,放弃这个男生吧。年轻人,我就一个要求,不要做海王,别来和我抢饭碗。后会有期!🦕🌊🌊🌊”


   🏄‍♂️随着海王的离开,天空也明亮了,太阳格外耀眼,因为我在海王的帮助下,成功撮合了三对暧昧情侣!


   💧怎么样?看完海王这一顿操作,你悟出了什么道理?没错,有机会要上,别要怂,没机会也要创造机会,尽量撮合更多的暧昧情侣。


   💧回顾海王的操作,我发现转移大法的精髓就是分配和递归寻找:


   💧我们需要用以下变量来维护这些操作,它们分别是:

boolean[][] line = new boolean[N][N];//邻接矩阵建立连线
int[] girl = new int[N];//存储每个女生被分配给了哪个男生  girl[1] = 2 --> 1号女生分配给了2号男生
boolean[] used;//这里标记过的意思是这次查找曾试图改变过该妹子的归属问题,但是没有成功,所以就不用瞎费工夫了

💧find()

private static boolean find(int x) {
        for (int j = 1; j <= m; j++) {//枚举每个女主
            if (line[x][j] && !used[j]) {//如果有暧昧并且还没有标记过
                used[j] = true;
                if (girl[j] == 0 || find(girl[j])) {//名花无主 或者 能腾出个位置来【递归实现】
                    girl[j] = x;
                    return true;
                }
            }
        }
        return false;
    }

在主函数这样做

 //看看能否给男生i分配一个女生
        for (int i = 1; i <= n; i++) {
            used = new boolean[N];//每次都给他new个新的,目的就是重置状态
            if (find(i)) ans++;
        }

🌊暧昧情侣代码如下:

public class 暧昧情侣 {
    static int N = (int) 1e4 + 10;
    static boolean[][] line = new boolean[N][N];//邻接矩阵建立连线
    static int[] girl = new int[N];//存储每个女生被分配给了哪个男生  girl[1] = 2 --> 1号女生分配给了2号男生
    static boolean[] used;//这里标记过的意思是这次查找曾试图改变过该妹子的归属问题,但是没有成功,所以就不用瞎费工夫了
    static int n, m, ans;
    static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    public static void main(String[] args) throws IOException {
        in.nextToken();
        n = (int) in.nval;//男
        in.nextToken();
        m = (int) in.nval;//女
        in.nextToken();
        int k = (int) in.nval;//k条记录,表示男女之间彼此之间有好感
        int temp;
        while (k-- > 0) {
            in.nextToken();
            temp = (int) in.nval;
            in.nextToken();
            line[temp][(int) in.nval] = true;
        }
        //看看能否给男生i分配一个女生
        for (int i = 1; i <= n; i++) {
            used = new boolean[N];
            if (find(i)) ans++;
        }
        System.out.println(ans);
    }
    private static boolean find(int x) {
        for (int j = 1; j <= m; j++) {//枚举每个妹子
            if (line[x][j] && !used[j]) {//如果有暧昧并且还没有标记过
                used[j] = true;
                if (girl[j] == 0 || find(girl[j])) {//名花无主 或者 能腾出个位置来【递归实现】
                    girl[j] = x;
                    return true;
                }
            }
        }
        return false;
    }
}

相关解释:

/**

*find(3), x=3, j=1, girl[j]=1(女士1已暂时被许配给了男士1)->find(girl[j])(进入递归,看能不能给男士1>再找个对象,把女士1让出来给男士3),

*find(1), used[1]=1,所以跳过j=1,进入j=2(男士1也很欣赏女士2),girl[j]=2(但女士2已暂时被许配给了男>士2) ->

*-> find(girl[j])(进入递归,看能不能给男士2再找个对象,把女士2让出来给男士1),

*find(2), used[2]=true,跳过j=1,j=2,进入j=3(男士2也很欣赏女士3),girl[j]=0(女士3单身), 正好,>girl[j]=2(女士3许配给男士2),return true,

*此时回溯到上一步find(1)这里面,既然find(girl[j])=True(给男士2重新找了个对象女士3)了,>girl[j]=1(女士2恢复单身了,和男士1在一起了),return true

*此时再回溯到上上一步find(3)这里面,既然find(girl[j])=True(给男士1找到了新对象女士2),girl[j]=3>(女士1恢复单身了,和男士3在一起了),return true

*

*至此由男士3单身引起的一系列递归和回溯结束了,并凑成了三对情侣。

*再回到大循环下,继续向下执行就好了

*/

🌊巩固加深

题目:完美牛棚

💧邻接矩阵解法

import java.io.*;
import java.util.Arrays;
public class Main {
    static int N = 210;
    static boolean[][] line = new boolean[N][N];
    static int[] match = new int[N];
    static boolean[] vis = new boolean[N];
    static int n, m;
    static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    public static void main(String[] args) throws IOException {
        in.nextToken();
        n = (int) in.nval;//牛
        in.nextToken();
        m = (int) in.nval;//棚
        for (int i = 1; i <= n; i++) {
            int count;
            in.nextToken();
            count = (int) in.nval;
            while (count-- > 0) {
                in.nextToken();
                line[i][(int) in.nval] = true;
            }
        }
        int res = 0;
        for (int i = 1; i <= n; i++) {
            Arrays.fill(vis, false);
            if (find(i)) res++;
        }
        System.out.println(res);
    }
    private static boolean find(int x) {
        for (int i = 1; i <= m; i++) {
            if (!vis[i] && line[x][i]) {
                vis[i] = true;
                if (match[i] == 0 || find(match[i])) {
                    match[i] = x;
                    return true;
                }
            }
        }
        return false;
    }
}

💧链式前向星解法

import java.io.*;
import java.util.Arrays;
public class Main {
    static int N = 210, M = N * N;//注意:最多有N * N条边
    static int[] match = new int[N];
    static boolean[] vis = new boolean[N];
    static int n, m;
    static int total;//第n条边,从1开始
    static int[] head = new int[M], next = new int[M], ends = new int[M];//这里不需要考虑边权
    static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    static void add(int start, int end) {
        ends[++total] = end;
        next[total] = head[start];
        head[start] = total;
    }
    public static void main(String[] args) throws IOException {
        in.nextToken();
        n = (int) in.nval;//牛
        in.nextToken();
        m = (int) in.nval;//棚
        Arrays.fill(head, -1);
        for (int i = 1; i <= n; i++) {
            in.nextToken();
            int count = (int) in.nval;
            while (count-- > 0) {
                in.nextToken();
                add(i, (int) in.nval);
            }
        }
        int res = 0;
        for (int i = 1; i <= n; i++) {
            Arrays.fill(vis, false);
            if (find(i)) res++;
        }
        System.out.println(res);
    }
    private static boolean find(int x) {
        for (int i = head[x]; i != -1; i = next[i]) {
            int j = ends[i];
            if (!vis[j]) {
                vis[j] = true;
                if (match[j] == 0 || find(match[j])) {
                    match[j] = x;
                    return true;
                }
            }
        }
        return false;
    }
}


🐳结语

🐬初学一门技术时,总有些许的疑惑,别怕,它们是我们学习路上的点点繁星,帮助我们不断成长。

🐟文章粗浅,希望对大家有帮助!

相关文章
|
7月前
|
安全 网络协议 数据建模
免费SSL证书最新申请全攻略
SSL证书分为三种类型:DV(域名验证型)适用于个人博客等,验证简单;OV(组织验证型)适用于电商、金融网站,需验证企业信息;EV(扩展验证型)提供更高信任级别。申请渠道有JoySSL(免费一年单域名证书)、Let&#39;s Encrypt(公共免费项目)和阿里云(免费DV证书,但有限制)。以JoySSL为例,申请流程包括注册账号、选择证书、填写信息、验证域名所有权、下载与安装。注意事项包括留意有效期、确保兼容性和使用最新版本证书,以保障网站安全。
|
10月前
|
存储 缓存 安全
Java 集合框架优化:从基础到高级应用
《Java集合框架优化:从基础到高级应用》深入解析Java集合框架的核心原理与优化技巧,涵盖列表、集合、映射等常用数据结构,结合实际案例,指导开发者高效使用和优化Java集合。
196 4
|
Web App开发 数据可视化 前端开发
Echart的使用初体验,Echarts的基本使用及语法格式,简单图表绘制和使用及图例添加【学习笔记】
本文介绍了ECharts的基本使用和语法格式,包括如何引入ECharts、创建容器、初始化echarts实例对象、配置option参数和一些基础图表的绘制方法。文章还提供了简单图表绘制和使用图例添加的示例代码,以及对ECharts特性和优势的概述。
Echart的使用初体验,Echarts的基本使用及语法格式,简单图表绘制和使用及图例添加【学习笔记】
|
11月前
|
IDE 网络安全 开发工具
IDE之vscode:连接远程服务器代码(亲测OK),与pycharm链接服务器做对比(亲自使用过了),打开文件夹后切换文件夹。
本文介绍了如何使用VS Code通过Remote-SSH插件连接远程服务器进行代码开发,并与PyCharm进行了对比。作者认为VS Code在连接和配置多个服务器时更为简单,推荐使用VS Code。文章详细说明了VS Code的安装、远程插件安装、SSH配置文件编写、服务器连接以及如何在连接后切换文件夹。此外,还提供了使用密钥进行免密登录的方法和解决权限问题的步骤。
4769 0
IDE之vscode:连接远程服务器代码(亲测OK),与pycharm链接服务器做对比(亲自使用过了),打开文件夹后切换文件夹。
|
机器学习/深度学习 算法 计算机视觉
经典神经网络论文超详细解读(五)——ResNet(残差网络)学习笔记(翻译+精读+代码复现)
经典神经网络论文超详细解读(五)——ResNet(残差网络)学习笔记(翻译+精读+代码复现)
5293 1
经典神经网络论文超详细解读(五)——ResNet(残差网络)学习笔记(翻译+精读+代码复现)
|
安全 网络安全 数据安全/隐私保护
智能家居安全指南:保护你的网络家园
在数字化时代,智能家居设备为我们带来了便捷生活的同时,也引入了安全隐患。本文以浅显易懂的语言,介绍了如何保护智能家居免受网络攻击的实用技巧。我们将从智能设备的密码设置、网络安全、软件更新等方面入手,教你如何打造一个安全的智能家居环境。无论你是科技小白还是资深玩家,都能从中获益。让我们一起守护数字生活的安全边界,享受智能技术带来的纯粹乐趣。
153 0
|
机器学习/深度学习 自然语言处理 算法
【Python机器学习专栏】文本数据的特征提取与表示
【4月更文挑战第30天】本文探讨了文本特征提取与表示在机器学习和NLP中的重要性。介绍了词袋模型、TF-IDF和n-gram等特征提取方法,以及稀疏向量和词嵌入等表示方式。Python中可利用sklearn和gensim库实现这些技术。有效的特征提取与表示有助于将文本数据转化为可处理的数值形式,推动NLP和机器学习领域的进步。
526 0
|
算法 数据挖掘 开发者
特征量化编码 | 学习笔记
快速学习特征量化编码,介绍了特征量化编码系统机制, 以及在实际应用过程中如何使用。
特征量化编码 | 学习笔记
|
XML 数据格式
小工具:批量替换文件夹下所有文件内容中的指定词
函数作用:找出某文件夹下的包含指定关键词文件列表,并将关键字修改为目标字并将新内容保存至源文件
473 0