最大二分匹配:最大“素数伴侣”问题

本文介绍了一种寻找数组中素数伴侣的最大数量的方法,利用最大二分匹配算法解决这一问题。通过构建合适的图模型,实现了有效的匹配并给出了完整的实现代码。


    “素数伴侣”:两个自然数的和为素数,则称这两个自然数为“素数伴侣”。现在给定一个数组,内含N<=100个自然数,自然数的范围是[2-30000],求出从该数组中组合出的“素数伴侣”的最大个数(同一个数的使用不能超过重复出现的次数)。如数组 array[]={5,6,6,13,17},输出结果是2。

    “素数伴侣”的特点是,成对的个元素一定在不同的集合当中,即“素数伴侣”的一个元素在奇数集合当中,另一个元素在偶数集合当中。像“素数伴侣”这种,问题的解来自于两个不相交的集合  且求能够匹配的最大个数的问题,是典型的最大二分匹配问题。最大二分匹配一个典型的实际应用是,把一个机器集合L和要同时执行的任务集合R相匹配。

    而最大二分匹配问题是多源多汇问题的一个特例,所以它可以完全使用“多源多汇”问题的流网络的解决方法来解决,其中最关键的环节在于寻找“增广路径”。“多源多汇”问题,一共3个集合,源点集合S,汇点集合D,其它点集合P。最大二分匹配问题没有集合P,只有集合S和集合D。同样,最大二分匹配最关键的环节还是寻找“增广路径”。

    所以,对最大二分匹配问题的理解和掌握,在于寻找“增广路径”,增广路径也是一条从集合S到集合D的路径,但是它可以反向,从而反向调节之前已经找好的路径。我用一张图简单表示一下什么是“增广路径”,如下:


增广路径的特点是,总会使匹配个数增加,如果找不到增广路径,说明匹配个数达到最大


下面是“素数伴侣”的代码,其中的最大二分匹配的方法的代码是通用的。

#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
#include <math.h>
using namespace std;
// 判断是否为素数,并将素数存到集合 primes 当中
bool isPrime(int num, set<int>& primes)
{
	if (num % 2 == 0) return false;
	int sq = static_cast<int>(sqrt(num));
	for (int i = 3; i < sq; i += 2)
		if (num % i == 0) return false;
	primes.insert(num);
	return true;
}
// 最大二分匹配
int dfs(vector<vector<int> >& edge, vector<int>& book, vector<int>& match, int u)
{
	for (int i = 0; i < book.size(); i++)
		if (book[i] == 0 && edge[u][i] == 1) {// edge[u][i] == 1 表示存在 u 到 i 的路径(即素数伴侣),book[i] == 0 表示 i 结点 未被访问
			book[i] = 1;// 访问 i 结点,标记为 1
			if (match[i] == -1 || dfs(edge, book, match, match[i])) {// 使用递归的方式寻找增广路径
				match[i] = u;
				match[u] = i;
				return 1;// 找到增广路径,调节之前的路径,并返回 1
			}
		}
	return 0;// 没有找到增广路径,返回 0
}
// 求最大个数的素数对,返回素数对的最大个数
int getPrimePairs(vector<int>& nums)
{
	sort(nums.begin(), nums.end());
	int len = nums.size();
	set<int> primes;// 素数集合
	vector<vector<int> > edge(len, vector<int>(len, 0));// 所有集合S到集合D的所有原始路径,即所有可能的“素数伴侣”
	for (int i = 0; i < len; i++)
		for (int j = i + 1; j < len; j++)
			if (primes.find(nums[i] + nums[j]) != primes.end() || isPrime(nums[i] + nums[j], primes)) {
				edge[i][j] = 1;// == 1 表示 nums[i] 和 nums[j] 构成“素数伴侣”
				edge[j][i] = 1;
			}
	int res = 0;
	vector<int> book(len, 0);// 访问的标记数组
	vector<int> match(len, -1);// 记录匹配的数据,match[i] == -1 表示 i 结点还没有被匹配
	for (int i = 0; i < len; i++) {
		if (nums[i] % 2 == 1) continue;// 选择偶数集合的元素
		res += dfs(edge, book, match, i);// 找到增广路径 +1,没有找到增广路径 +0
		for (int k = 0; k < len; k++) book[k] = 0;
	}
	return res;
}


int main()
{
	int n, num;
	cin >> n;
	vector<int> nums;
	for (int i = 0; i < n; i++) {
		cin >> num;
		nums.push_back(num);
	}
	cout << getPrimePairs(nums) << endl;
	return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值