使用C++实现求职者和部门之间最大配对

某人力资源公司收到了m个合格的求职者的简历,要将他们分发给n个部门,每份简历符合一个或者几个部门的要求,但是每个人的简历最多送给k个部门,每个部门最多可以接受d份简历,如何实现求职者和部门之间的最大配对。使用了最大流算法来解决问题,其中将每份简历最多送给k个部门和每个部门最多接受d份简历的问题通过构建一个流网络来解决。我们使用Ford-Fulkerson算法来实现这个最大流问题的解决。

#include <iostream>  // 包含输入输出流库
#include <vector>    // 包含vector容器库
#include <cstring>   // 包含C风格字符串处理函数
#include <queue>     // 包含队列数据结构
#include <algorithm> // 包含常用算法函数,如min
using namespace std; // 使用标准命名空间

class MaxFlow { // 定义最大流类
    struct Edge { // 定义边结构
        int to, capacity, flow, rev; // 目标节点、容量、流量、反向边索引
    };
    
    int n; // 节点数
    vector<vector<Edge>> adj; // 邻接表表示图
    vector<int> level; // 节点层次
    vector<int> ptr; // 当前弧指针
    
    bool bfs(int s, int t) { // 宽度优先搜索构建层次图
        queue<int> q; // 创建队列
        level.assign(n, -1); // 初始化所有节点层次为-1
        level[s] = 0; // 源点层次为0
        q.push(s); // 源点入队
        
        while (!q.empty()) { // 当队列非空时
            int u = q.front(); // 取出队首元素
            q.pop(); // 队首元素出队
            for (const Edge& e : adj[u]) { // 遍历u的所有邻边
                if (level[e.to] == -1 && e.flow < e.capacity) { // 如果目标节点未访问且边未满
                    level[e.to] = level[u] + 1; // 设置目标节点层次
                    q.push(e.to); // 目标节点入队
                }
            }
        }
        return level[t] != -1; // 返回是否存在增广路径
    }
    
    int dfs(int u, int t, int pushed) { // 深度优先搜索寻找增广路径
        if (pushed == 0 || u == t) return pushed; // 如果到达终点或没有可增广的流量,返回
        
        for (int& cid = ptr[u]; cid < adj[u].size(); ++cid) { // 遍历u的所有邻边
            Edge& e = adj[u][cid]; // 获取当前边
            int v = e.to; // 获取目标节点
            if (level[u] + 1 == level[v] && e.flow < e.capacity) { // 如果是下一层且边未满
                int tr = dfs(v, t, min(pushed, e.capacity - e.flow)); // 递归寻找增广路径
                if (tr == 0) continue; // 如果没有找到增广路径,继续下一条边
                e.flow += tr; // 正向边增加流量
                adj[v][e.rev].flow -= tr; // 反向边减少流量
                return tr; // 返回增广流量
            }
        }
        return 0; // 没有找到增广路径,返回0
    }
    
public:
    MaxFlow(int n) : n(n), adj(n), level(n), ptr(n) {} // 构造函数初始化
    
    void addEdge(int u, int v, int capacity) { // 添加边
        Edge a = {v, capacity, 0, adj[v].size()}; // 创建正向边
        Edge b = {u, 0, 0, adj[u].size()}; // 创建反向边
        adj[u].push_back(a); // 添加正向边
        adj[v].push_back(b); // 添加反向边
    }
    
    int getMaxFlow(int s, int t) { // 计算最大流
        int maxFlow = 0; // 初始化最大流为0
        while (bfs(s, t)) { // 当存在增广路径时
            ptr.assign(n, 0); // 重置当前弧指针
            while (int flow = dfs(s, t, INT_MAX)) { // 寻找增广路径
                maxFlow += flow; // 累加增广流量
            }
        }
        return maxFlow; // 返回最大流
    }
};

int main() {
    int m, n, k, d; // 定义变量:求职者数量、部门数量、最多投递数、最多接受数
    cout << "请输入求职者数量 m: "; // 提示输入求职者数量
    cin >> m; // 输入求职者数量
    cout << "请输入部门数量 n: "; // 提示输入部门数量
    cin >> n; // 输入部门数量
    cout << "请输入每个求职者最多投递的部门数量 k: "; // 提示输入最多投递数
    cin >> k; // 输入最多投递数
    cout << "请输入每个部门最多接受的简历数量 d: "; // 提示输入最多接受数
    cin >> d; // 输入最多接受数
    
    vector<vector<int>> preferences(m); // 创建二维vector存储求职者偏好
    cout << "请输入每个求职者的部门偏好(每行以空格分隔的部门编号,使用-1结束):\n"; // 提示输入偏好
    for (int i = 0; i < m; ++i) { // 遍历每个求职者
        int dept; // 临时存储部门编号
        while (cin >> dept && dept != -1) { // 输入部门编号直到-1
            preferences[i].push_back(dept); // 将部门编号添加到偏好列表
        }
    }
    
    int source = 0; // 源点编号
    int sink = m + n + 1; // 汇点编号
    MaxFlow maxFlow(m + n + 2); // 创建最大流对象
    
    for (int i = 0; i < m; ++i) { // 遍历每个求职者
        maxFlow.addEdge(source, i + 1, k); // 添加源点到求职者的边
    }
    
    for (int i = 0; i < n; ++i) { // 遍历每个部门
        maxFlow.addEdge(m + 1 + i, sink, d); // 添加部门到汇点的边
    }
    
    for (int i = 0; i < m; ++i) { // 遍历每个求职者
        for (int dept : preferences[i]) { // 遍历求职者的偏好部门
            maxFlow.addEdge(i + 1, m + 1 + dept, 1); // 添加求职者到部门的边
        }
    }
    
    int maxMatching = maxFlow.getMaxFlow(source, sink); // 计算最大流
    cout << "最大配对数量: " << maxMatching << endl; // 输出最大配对数量
    
    return 0; // 程序正常结束
}
  • MaxFlow类实现了最大流算法,包括构造函数、添加边的方法和计算最大流的方法。
  • bfs方法用于构建层次图,dfs方法用于寻找增广路径。
  • main函数读取输入,包括求职者数量、部门数量、每个求职者最多投递的部门数量和每个部门最多接受的简历数量。
  • 使用邻接表存储图,并添加相应的边。
  • 最后调用getMaxFlow方法计算并输出最大配对数量。

在求职者和部门之间最大配对的问题中,如果简历和部门的要求不是匹配与否,而是有一个介于0和1之间的匹配度,我们可以使用一种称为“加权二分图最大匹配”的算法。这个问题可以转化为一个最大权匹配问题,可以使用匈牙利算法或KM算法(Kuhn-Munkres)来解决。

基本思路

  1. 建模问题: 将求职者和部门建模为一个二分图,边的权重为匹配度。
  2. KM算法: 通过KM算法求解最大权匹配问题。
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAXN = 500; // 假设最大顶点数为500
const int INF = 1e9; // 一个很大的值,表示无穷大

class KM {
    int n; // 顶点数量
    vector<vector<double>> weights; // 权重矩阵
    vector<double> lx, ly; // 顶点标号
    vector<int> match; // 匹配结果
    vector<bool> S, T; // S集合和T集合
    vector<double> slack; // 松弛变量
    vector<int> slackx; // 松弛变量对应的x
    
    bool bfs(int u) {
        S[u] = true; // 标记u属于S集合
        for (int v = 0; v < n; ++v) {
            if (T[v]) continue;
            double delta = lx[u] + ly[v] - weights[u][v];
            if (abs(delta) < 1e-9) { // 绝对值比较,防止浮点数精度问题
                T[v] = true; // 标记v属于T集合
                if (match[v] == -1 || bfs(match[v])) {
                    match[v] = u;
                    return true;
                }
            } else {
                if (slack[v] > delta) {
                    slack[v] = delta;
                    slackx[v] = u;
                }
            }
        }
        return false;
    }
    
    void update() {
        double delta = INF;
        for (int v = 0; v < n; ++v) {
            if (!T[v]) {
                delta = min(delta, slack[v]);
            }
        }
        for (int i = 0; i < n; ++i) {
            if (S[i]) lx[i] -= delta;
            if (T[i]) ly[i] += delta;
            if (!T[i]) slack[v] -= delta;
        }
    }
    
public:
    KM(int n) : n(n), weights(n, vector<double>(n)), lx(n), ly(n), match(n, -1), S(n), T(n), slack(n), slackx(n) {}
    
    void setWeight(int u, int v, double w) {
        weights[u][v] = w;
    }
    
    double maxWeightMatching() {
        for (int i = 0; i < n; ++i) {
            lx[i] = *max_element(weights[i].begin(), weights[i].end());
        }
        
        for (int u = 0; u < n; ++u) {
            fill(slack.begin(), slack.end(), INF);
            while (true) {
                fill(S.begin(), S.end(), false);
                fill(T.begin(), T.end(), false);
                if (bfs(u)) break;
                update();
            }
        }
        
        double result = 0;
        for (int v = 0; v < n; ++v) {
            if (match[v] != -1) {
                result += weights[match[v]][v];
            }
        }
        return result;
    }
};

int main() {
    int m, n;
    cout << "请输入求职者数量 m: ";
    cin >> m;
    cout << "请输入部门数量 n: ";
    cin >> n;
    
    KM km(max(m, n)); // 创建KM算法对象,顶点数量为求职者和部门数量中的较大值
    
    cout << "请输入匹配度矩阵(" << m << "行" << n << "列,介于0和1之间):\n";
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) {
            double weight;
            cin >> weight;
            km.setWeight(i, j, weight);
        }
    }
    
    double maxMatching = km.maxWeightMatching();
    cout << "最大匹配度: " << maxMatching << endl;
    
    return 0;
}

代码解释

  • KM类: 实现了Kuhn-Munkres算法。包括初始化、设置权重和求解最大权匹配的功能。
  • bfs函数: 使用广度优先搜索寻找增广路径。
  • update函数: 更新顶点标号,调整松弛变量。
  • maxWeightMatching函数: 计算最大权匹配。
  • main函数: 读取输入,创建KM算法对象,设置权重矩阵,并计算最大权匹配。

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

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

相关文章

昆虫学(书籍学习资料)

包括昆虫分类&#xff08;上下册&#xff09;、昆虫生态大图鉴等书籍资料。

搜索+动态规划

刷题刷题刷题刷题 ​​​​​​​​​​​​​​Forgery - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 思路&#xff1a; 需要两个数组&#xff0c;一个数组全部初始化为".",另一个数组输入数据&#xff0c;每碰到一个“.”就进行染色操作&#xff0c;将其周围的…

Django学习第五天

启动项目命令 python manage.py runserver 图像验证码生成随机字母或者数字 import random from PIL import Image, ImageDraw, ImageFont, ImageFilterdef check_code(width120, height40, char_length5, font_fileZixunHappyBold.ttf, font_size28):code []img Image.new…

在C#/Net中使用Mqtt

net中MQTT的应用场景 c#常用来开发上位机程序&#xff0c;或者其他一些跟设备打交道比较多的系统&#xff0c;所以会经常作为拥有数据的终端&#xff0c;可以用来采集上传数据&#xff0c;而MQTT也是物联网常用的协议&#xff0c;所以下面介绍在C#开发中使用MQTT。 安装MQTTn…

【ARMv8/v9 GIC 系列 5.1 -- GIC GICD_CTRL Enable 1 of N Wakeup Function】

请阅读【ARM GICv3/v4 实战学习 】 文章目录 GIC Enable 1 of N Wakeup Function基本原理工作机制配置方式应用场景小结GIC Enable 1 of N Wakeup Function 在ARM GICv3(Generic Interrupt Controller第三代)规范中,引入了一个名为"Enable 1 of N Wakeup"的功能。…

图像的对数变换

对数变换在图像处理中通常有以下作用&#xff1a; 因为对数曲线在像素值较低的区域斜率较大&#xff0c;像素值较高的区域斜率比较低&#xff0c;所以图像经过对数变换之后&#xff0c;在较暗的区域对比度将得到提升&#xff0c;因而能增强图像暗部的细节。图像的傅里叶频谱其…

Ubuntu多显示器设置不同缩放比例

Ubuntu多显示器设置不同缩放比例 设备问题解决方案 设备 笔记本屏幕分辨率为2560 \times 1600&#xff0c;外接显示器的分辨率为3840 \times 2160。 问题 Ubuntu默认的显示器设置中&#xff0c;缩放仅能选择100%&#xff0c;200%&#xff0c;300%&#xff0c;400%。假…

C++中的引用——引用做函数参数

作用&#xff1a;函数传参时&#xff0c;可以利用引用的技术让形参修饰实参 优点&#xff1a;可以简化指针修改实参 示例&#xff1a; 1.值传递 运行结果&#xff1a; 2.地址传递 运行结果&#xff1a; 3.引用传递 运行结果&#xff1a;

ES6模块化学习

1. 回顾&#xff1a;node.js 中如何实现模块化 node.js 遵循了 CommonJS 的模块化规范。其中&#xff1a; 导入其它模块使用 require() 方法 模块对外共享成员使用 module.exports 对象 模块化的好处&#xff1a; 大家都遵守同样的模块化规范写代码&#xff…

一对一服务,定制化小程序:NetFarmer助力企业精准触达用户

在当今这个日新月异的数字化时代&#xff0c;小程序以其独特的魅力和广泛的应用场景&#xff0c;正逐步成为企业出海战略中的璀璨明星。NetFarmer&#xff0c;作为业界领先的数字化出海服务商&#xff0c;不仅深谙HubSpot营销自动化的精髓&#xff0c;更在小程序领域展现了卓越…

【UE5.3】笔记8 添加碰撞,检测碰撞

添加碰撞 打开BP_Food,添加Box Collision组件&#xff0c;与unity类似&#xff1a; 调整Box Collision的大小到刚好包裹物体&#xff0c;通过调整缩放和盒体范围来控制大小&#xff0c;一般先调整缩放找个大概大小&#xff0c;然后调整盒体范围进行微调。 碰撞检测 添加好碰撞…

CTF常用sql注入(二)报错注入(普通以及双查询)

0x05 报错注入 适用于页面无正常回显&#xff0c;但是有报错&#xff0c;那么就可以使用报错注入 基础函数 floor() 向下取整函数 返回小于或等于传入参数的最大整数。换句话说&#xff0c;它将数字向下取整到最接近的整数值。 示例&#xff1a; floor(3.7) 返回 3 floor(-2…

Python脚本:将Word文档转换为Excel文件

引言 在文档处理中&#xff0c;我们经常需要将Word文档中的内容转换成其他格式&#xff0c;如Excel&#xff0c;以便更好地进行数据分析和报告。针对这一需求&#xff0c;我编写了一个Python脚本&#xff0c;能够批量处理指定目录下的Word文档&#xff0c;将其内容结构化并转换…

pandas,dataframe使用笔记

目录 新建一个dataframe不带列名带列名 dataframe添加一行内容查看dataframe某列的数据类型新建dataframe时设置了列名&#xff0c;则数据类型为object dataframe的保存保存为csv文件保存为excel文件 dataframe属于pandas 新建一个dataframe 不带列名 df pd.DataFrame() 带…

【C++】unordered系列容器的封装

你很自由 充满了无限可能 这是很棒的事 我衷心祈祷你可以相信自己 无悔地燃烧自己的人生 -- 东野圭吾 《解忧杂货店》 unordered系列的封装 1 unordered_map 和 unordered_set2 改造哈希桶2.1 模版参数2.2 加入迭代器 3 上层封装3.1 unordered_set3.2 unordered_map 4 面…

C++基础22 字符串与字符数组及其相关操作

这是《C算法宝典》C基础篇的第22节文章啦~ 如果你之前没有太多C基础&#xff0c;请点击&#x1f449;C基础&#xff0c;如果你C语法基础已经炉火纯青&#xff0c;则可以进阶算法&#x1f449;专栏&#xff1a;算法知识和数据结构&#x1f449;专栏&#xff1a;数据结构啦 ​ 目…

c++重定向输出和输出(竞赛讲解)

1.命令行重定向 在命令行中指定输出文件 指令 .\重定向学习.exe > 1.txt 效果 命令行输入和输出 指令 .\重定向学习.exe < 2.txt > 1.txt 效果 代码 #include<bits/stdc++.h> using namespace std; int n; int main(){cin>>n;for(int i=0;i<n;i…

4、SSD主控

简述 主控是个片上系统&#xff0c;由硬件和固件组成一个功能完整的系统&#xff1b;上文所述的FTL就属于主控的固件范畴。主控闪存构成了整个SSD&#xff0c;在闪存确定的情况下&#xff0c;主控就反映了各家SSD的差异。实时上各家SSD的差异也主要反应在主控上&#xff0c;毕…

VMware虚拟机Ubuntu网络有线线缆已拔出问题

1、问题描述 VMware虚拟机Ubuntu不能联网&#xff0c;打开设置中&#xff0c;网络显示“有线 线缆已拔出”。 2、查看虚拟网络连接 查看主机的网络连接&#xff0c;确保虚拟网络已启用。 3、启动虚拟机网络服务 打开主机的 ‘服务’&#xff08;winr&#xff0c;运行框中输入…

46.修复HOOK对代码造成的破坏

上一个内容&#xff1a;45.使用hook点链表实现指定跳转 以 45.使用hook点链表实现指定跳转 它的代码为基础进行修改 此代码已实现无敌与秒杀功能 HOOKPOINT.h文件里的修改 #pragma oncetypedef struct CPUINFO {unsigned eflags;unsigned edi;unsigned esi;unsigned ebp;un…