二分图最大匹配(匈牙利算法):素数伴侣

题目描述

  若两个正整数的和为素数,则这两个正整数称之为“素数伴侣”,如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个点,如果这两个数加起来的和是素数,给这两个数中间连一条边。之后,我们要选择尽可能多的边,要求这些边不共享端点。每个数的取值范围是[2,30000]。素数除了2是偶数,其他是奇数——而现在不可能出现2了,所以我们只考虑奇数的素数。2个数的和是奇数,只有奇数+偶数。所以,我们把这些数分成2堆——奇数和偶数,得到二分图。然后在他们中间,和是素数的,连上一条边,然后做匹配。对二分图的最大匹配,有一个简单很多的算法, 匈牙利算法

相关概念

二分图
  二分图其实就是在一个图中所有的点可以分为两组,同一组中没有边,所有的边都跨越了两个组。准确的说:把一个图的顶点划分为两个不相交的集合 U 和 V ,且使得每一条边都分别连接 U 、V 中的顶点,如果存在这样的划分,则称此图为二分图。
  此外二分图还有一个等价定义是:不含有「含奇数条边的环」的图。
匹配
  二分图匹配就是边的集其中任意两条边没有公共顶点。我们定义有:
       匹配边、匹配点、非匹配边、非匹配点。
  如图:若1—2相连,5—4相连,7—6相连。则显然1—2边、5—4边、7—6边为匹配边,1、2、4、5、6、7为匹配点。剩下的为非匹配点和非匹配边。

最大匹配
  一个图的匹配中所含边数最多的匹配即为此图最大匹配。像上图最大匹配即为:


  显然最大匹配可能不只有一种。
完美匹配
  如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配。上图都是完美匹配。显然,完美匹配一定是最大匹配(完美匹配的任何一个点都已经匹配,添加一条新的匹配边一定会与已有的匹配边冲突)。但并非每个图都存在完美匹配。
交替路径
  从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边……形成的路径叫交替路径。如图2—>1—>8—>7即为一条交替路径。

增广路径
  从一个未匹配点出发,走交替路径,如果到达另一个未匹配点,则这条交替路径称为增广路径(agumenting path)。如图1—>2—>3—>6—>7—>8即为一条增广路径。

增广路径性质
  由增广路的定义可以推出下述三个结论(设当前路径为P,当前匹配为M):
  1、P的路径长度必定为奇数,第一条边和最后一条边都不属于M,因为两个端点分属两个集合,且未匹配。
  2、P经过取反操作可以得到一个更大的匹配M’。
  3、M为G的最大匹配当且仅当不存在相对于M的增广路径。

匈牙利算法:用增广路径求最大匹配
  1、置M为空 。
  2、找出一条增广路径P,通过取反操作获得更大的匹配M’代替M 3、重复2操作直到找不出增广路径为止 。

寻找增广路径算法
  我们采用DFS的办法找一条增广路径,从X部一个未匹配的顶点u开始,找一个未访问的邻接点v(v一定是Y部顶点)。对于v,分两种情况:
  1、如果v未匹配,则已经找到一条增广路。
  2、如果v已经匹配,则取出v的匹配顶点w(w一定是X部顶点),边(w,v)目前是匹配的,根据“取反”的想法,要将(w,v)改为未匹配,(u,v)设为匹配,能实现这一点的条件是看从w为起点能否新找到一条增广路径P’。如果行,则u-v-P’就是一条以u为起点的增广路径。
代码如下

 1 #include<iostream>
 2 #include<cstring>
 3 #include<vector>
 4 using namespace std;
 5 const int N = 100, M = 60001;
 6 vector<int> g[N]; //二分图
 7 int pre[N], nums[N];  //偶数对应的当前奇数匹配点,输入的数
 8 bool isPrime[M], flag[N]; //素数对应表,可匹配点是否已匹配
 9 //欧拉筛表建立素数对应表
10 void primeTab() {
11     int prime[M], cnt = 0;
12     prime[cnt++] = 2;
13     memset(isPrime, true, sizeof(isPrime));
14     isPrime[0] = isPrime[1] = false;
15     for (int i = 4; i < M; i += 2)
16         isPrime[i] = false;
17     for (int i = 3; i < M; i += 2) {
18         if (isPrime[i])
19             prime[cnt++] = i;
20         for (int j = 0; j < cnt && i * prime[j] < M; ++j) {
21             isPrime[i * prime[j]] = false;
22             if (i % prime[j] == 0)
23                 break;
24         }
25     }
26 }
27 //深度优先搜索增广路径,从而找到最大匹配
28 bool dfs(int n) {
29     for (int i = 0; i < g[n].size(); ++i) {
30         int x = g[n][i];
31         if (flag[x])   //若nums[x]是nums[n]已经匹配的点,现在需要将其腾出
32             continue;
33         flag[x] = true; //nums[x]成为nums[n]的匹配点
34         //nums[x]未被匹配||nums[x]已被匹配但可以让其匹配点寻找新的匹配点从而腾出nums[x]
35         if (pre[x] == -1 || dfs(pre[x])) {  
36             pre[x] = n;   //给nums[n]匹配nums[x]
37             return true;
38         }
39     }
40     return false;
41 }
42 int main() {
43     int n;
44     primeTab(); //建立素数对应表
45     /*
46     pre默认赋为-1而非0,这是因为pre存储的是奇数在nums中的下标。
47     若pre默认赋0则pre[i]=0有两种意义:
48     (1)pre[i]映射到无;(2)pre[i]映射到nums[0]。
49     互相矛盾,故赋-1。
50     */
51     memset(pre, -1, sizeof(pre)); 
52     while (cin >> n) {
53         for (int i = 0; i < n; ++i)
54             cin >> nums[i];
55         for (int i = 0; i < n - 1; ++i)
56             for (int j = i + 1; j < n; ++j)
57                 if (isPrime[nums[i] + nums[j]])
58                     nums[i] & 1 ? g[i].push_back(j) : g[j].push_back(i);  //链表法建立二分图,奇数链接偶数可匹配点
59         int ans = 0;
60         for (int j = 0; j < n; ++j) {
61             memset(flag, false, sizeof(flag));
62             if (dfs(j))
63                 ++ans;
64         }
65         cout << ans << endl;
66         for (int i = 0; i < n; ++i) {  //清空二分图映射关系
67             g[i].clear();
68         }
69         memset(pre, -1, sizeof(pre));   //清空偶数对应的当前奇数匹配点
70     }
71 }

猜你喜欢

转载自www.cnblogs.com/xiehuazhen/p/12574797.html
今日推荐