您的位置:首页 > 其它

素数伴侣问题—>二部图最大匹配问题

2017-04-26 16:41 344 查看

题目描述

若两个正整数的和为素数,则这两个正整数称之为“素数伴侣”,如2和5、6和13,它们能应用于通信加密。现在密码学会请你设计一个程序,

从已有的N(N为偶数)个正整数中挑选出若干对组成“素数伴侣”,挑选方案多种多样,例如有4个正整数:2,5,6,13,如果将5和6分为一组中只能得到一组“素数伴侣”,

而将2和5、6和13编组将得到两组“素数伴侣”,能组成“素数伴侣”最多的方案称为“最佳方案”,当然密码学会希望你寻找出“最佳方案”。

输入:

有一个正偶数N(N≤100),表示待挑选的自然数的个数。后面给出具体的数字,范围为[2,30000]。

输出:

输出一个整数K,表示你求得的“最佳方案”组成“素数伴侣”的对数。

输入描述:

输入说明

1 输入一个正偶数n

2 输入n个整数

输出描述:

求得的“最佳方案”组成“素数伴侣”的对数。

输入例子:

4

2 5 6 13

输出例子:

2

算法思想

考虑使用图论的方法来给这个题建模。 100个数看成100个点,如果这两个数加起来的和是素数,给这两个数中间连一条边。 之后,我们要选择尽可能多的边,要求这些边不共享端点。

——这个东西呢,叫做一般无向图的最大匹配。

但是,一般无向图的最大匹配,这个算法的难度,有点,大……

这个问题,合适的算法叫,带花树开花算法。

具体参见:http://blog.csdn.net/yihuikang/article/details/10460997

http://fanhq666.blog.163.com/blog/static/8194342620120304463580/

现场推完这么多,写一个正确的,有点不科学……

我们考虑再分析一下这个题 注意到,每个数的取值范围是[2,30000]

素数,除了2是偶数,其他是奇数——而现在不可能出现2了,所以我们只考虑奇数的素数 2个数的和是奇数,有什么情况呢?

只有奇数+偶数 所以,我们把这些数分成2堆——奇数和偶数,然后在他们中间,和是素数的,连上一条边,然后做匹配。

——肯定有人会说,你这个和前面的建图有什么本质不同的地方吗?

——是没有,但是我们说明了得到的图一定是二分图,这是有意义的。

因为对二分图的最大匹配,有一个简单很多的算法,匈牙利算法。

我们先说明一下,什么是二分图。

二分图就是,你可以把图上的点分成2堆,每堆之内的点不会有边,2堆之间,才可能连边。换句话说,一条边,必定连2个来自不同堆的点。

现在,对每条边,一定连一个奇数,一个偶数,点能按奇数和偶数分成两部分,刚好就是二分图嘛!

有关二分图匹配的匈牙利算法,具体请自行搜索,这里扯一下我个人对这个算法的理解。

外层,暴力考虑左边每个点

对枚举的每个左边的点,要找右边一个点来匹配。

那就是对左边的点,我们看他连出去的边,或者说,能连到的右边的点

有2种情况:

1、右边的点没人匹配——我直接贪心匹配上去

2、右边的点有人匹配——考虑把目前与这个右边的点 x 匹配的左边的点

pre[x],重新匹配一个其他的点,如果成功了,那pre[x]原来匹配的点x就释放开了,我可以直接占领上去。

最后统计匹配成功多少个左边的点就行了。

代码块

#include <iostream>
#include <vector>
#include <string>
#include <math.h>
using namespace std;

vector<int> G[100];
bool visited[100];
int pre[100];

bool isPrime(int k)
{
if (k <= 2)
{
return true;
}
for (int i = 2; i <= sqrt(k); i++)
{
if (k%i == 0)
{
return false;
}
}
return true;
}

bool dfs(int k)
{
int x;
for (int i = 0; i < G[k].size(); ++i)
{
x = G[k][i];
if (visited[x]) continue;
else
visited[x] = true;
if (pre[x] == 0 || dfs(pre[x]))
{
pre[x] = k;
return true;
}
}
return false;
}

int main()
{
int num;
while (cin >> num)
{
int *nums = new int[num + 1];
for (int i = 1; i <= num; ++i)
{
cin >> nums[i];
}
for (int i = 1; i <= num; ++i)
{
for (int j = i + 1; j <= num; ++j)
{
if (isPrime(nums[i] + nums[j]))
{
(nums[i] & 1) ? G[i].push_back(j) : G[j].push_back(i);
}
}
}
int count = 0;
memset(pre, 0, sizeof(pre));
for (int i = 1; i <= num; ++i)
{
memset(visited, false, sizeof(visited));
if (dfs(i)) count++;
}

cout << count << endl;
for (int i = 1; i <= num; i++)
{
G[i].clear();
}
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: