Air Raid --- 二分图匹配 匈牙利算法

问题描述
考虑一个城镇,所有街道都是单向的,每条街道都从一个十字路口通向另一个十字路口。众所周知,从一个十字路口开始穿过城镇的街道,您永远无法到达同一个十字路口,即城镇的街道不形成循环。
有了这些假设,你的任务是编写一个程序,找出可以降落在城镇上的最小数量的伞兵,并以这样的方式访问这个城镇的所有交叉点,这样一个以上的伞兵就不会访问任何交叉点。每个伞兵降落在一个十字路口,可以沿着城镇街道访问其他十字路口。每个伞兵的起始路口没有限制。

输入
您的程序应该读取数据集。输入文件的第一行包含数据集的数量。每个数据集指定一个城镇的结构,格式如下:
no_of_intersections
no_of_streets
S1 E1
S2 E2

Sno_of_streets Eno_of_streets
每个数据集的第一行包含一个正整数 no_of_intersections(大于 0 且小于等于 120),即城镇的交叉口数量。第二行包含一个正整数 no_of_streets,它是该镇的街道数。接下来的 no_of_streets 线,对应城镇中的每条街道,是随机排序的,代表城镇的街道。与街道 k (k <= no_of_streets) 对应的行由两个正整数组成,由一个空格分隔: Sk (1 <= Sk <= no_of_intersections) - 作为街道起点的交叉口编号,以及 Ek ( 1 <= Ek <= no_of_intersections) - 街道尽头的交叉口编号。交点由从 1 到 no_of_intersections 的整数表示。
连续的数据集之间没有空行。输入数据正确。

输出
程序的结果在标准输出上。对于每个输入数据集,程序在一行上打印,从行首开始,一个整数:访问镇上所有十字路口所需的最少伞兵人数。

样本输入
2
4
3
3 4
1 3
2 3
3
3
1 3
1 2
2 3

样本输出
2
1

最少伞兵数 = 节点数 - 匹配数
因为若一个都没有匹配,则需要的伞兵数量为节点总数
而每匹配一次,则会减少一个伞兵

#include<bits/stdc++.h>
using namespace std;

int t, n, m, x, y, ans;
int g[125][125];  // 二维矩阵
int vis[125];  // 标记右节点是否被匹配
int linker[125];  // 下标为右节点,存入右节点的左匹配节点值

bool dfs(int u) {  // 搜索每个左节点的匹配对象
	for(int i = 1; i <= n; i++) {  // 遍历右节点
		if(!vis[i] && g[u][i]) {  // 若没有访问 并且 可能匹配成功
			vis[i] = 1;  // 注意标记
			// 若该右节点还没有匹配对象,或者该右节点的匹配对象左节点,可以找到新的匹配对象
			if(!linker[i] || dfs(linker[i])) {  
				linker[i] = u;  // 记录, 这里的记录是为了后续可能会更改匹配对象而记录
				return true; 
			}
		}
	}
	return false;
}

int hungary() {
	ans = 0;
	memset(linker, 0, sizeof(linker));  // 注意初始化位置
	for(int i = 1; i <= n; i++) {  // 遍历所有的左节点
		memset(vis, 0, sizeof(vis));  // 注意初始化位置
		if(dfs(i))	ans++;  // 为左节点进行匹配
	}
	return ans;
}

int main() {
	scanf("%d", &t);
	while(t--) {
		memset(g, 0, sizeof(g));
		scanf("%d", &n);  // 十字路口数量即,节点个数
		scanf("%d", &m);  // 街道数量即, 匹配数量
		for(int i = 0; i < m; i++) {
			scanf("%d %d", &x, &y);
			g[x][y] = 1;
		} 
		printf("%d\n", n - hungary());  // 返回n - ans
	}
	return 0;
} 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值