题目描述

题目描述
若两个正整数的和为素数,则这两个正整数称之为“素数伴侣”,如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个整数

输出描述:

 

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

示例1

输入

复制

4
2 5 6 13

输出

复制

2

【问题分析】:

从给定的数中取两个数,这两个数的和满足素数,完成一个匹配,容易知道,其中一个数为偶数,另一个数为奇数

(否则和为偶数,必不可能为素数)

因此将原先的数分成两部分,其中一部分为奇数,另一部分为偶数。

原问题变为:完成从《奇数,偶数》的勾连(勾连的条件为和为素数),使得最终匹配的总数最多

该问题即为二分图的最大匹配,二分图中包含两部分点,每一个点只能与另一部分点中的部分相连

连接两部分中若干点,即两两配对,求最大匹配数。

匈牙利算法:

将点分成上下两部分,奇数arr1[]  和   偶数arr2[]

每一轮考察上面的一个点U   遍历下方的点,对于其中一个点V  若(u,v)能够匹配(如组合成素数) 并且 本轮没有访问到该点

  • 那么判断 v点是否已经匹配过,如果没有那么直接匹配
  • 如果v点之前匹配过(match[v]!=-1) 那么v点试图抛弃match[v] (即match[v]点能够找到其他匹配的点) 若成功,那么直接抛弃

实现逻辑:


bool find(int u)  //奇数第u个寻找偶数匹配  组配成素数组合
{for (int v = 0; v < N; v++){//u v可以组配成素数组合  并且本轮寻找增广路  v还没被讨论(每一轮都要初始化)if (map[u][v] && !visit[v]){visit[v] = true;//若v点还没有匹配(=-1)  或者能够将匹配任务推卸给 v之前的匹配对象if (match[v] == -1 || find(match[v])){match[v] = u;  //构建新的匹配return true;}}}return false;}

关键易错点:

visit数组的理解,其在每一轮都需要初始化为false,  一轮可以理解为一个奇数进行匹配,

另外一个实现是素数的判断:

任何一个整数可以划分为6i 6i+1 6i+2 6i+3 6i+4 6i+5

首先6i 6i+2 6i+3 6i+4的类别的数一定不是素数,因为可以被2 或者 3整除

因此只有满足一个数num  其 num%6==1 或  num%6==5才可能为素数

接下来从i=2 循环到 i=sqrt(num)  判断是不是i都不能被num整除

进一步优化:

其中一些i不需要判断,这些i满足 6k 6k+2 6k+3 6k+4  

反证法,如果这些i能被num整除,那么num一定也满足 6k 6k+2 6k+3 6k+4 而这与num一定为6k+1或6k+5不符合

bool isPrime(int num)
{int data = num;int loopNum = sqrt(num);for (int i = 2; i <= loopNum; i++){if (data % i == 0){return 0;}}return 1;if (num < 4)return num > 1;//任何的数可以划分为  6i 6i+1 6i+2 6i+3 6i+4 6i+5   //可以看到只能是6i+1 6i+5可能是素数if (num % 6 != 1 && num % 6 != 5)return false;//接下来验证 从i=2到i=sqrt(Num)是否能被Num整除//可以排除 被除数为   6i+2 6i+3 6i+4 6i   反证:如果num能被6i整除  那么num是6的倍数  与num=6i+1或6i+5不符合for (int i = 5; i < sqrt(num); i += 6){if (num % (i) == 0 || num % (i + 2) == 0)return false;}return true;}

如何统计最大匹配数,

即针对每一个奇数,调用find() 若返回true  那么count++

而每一轮结束,初始化visit={false}

完整代码:

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<math.h>
using namespace std;
int M, N;
bool visit[100] = { false };//标记一轮中待匹配元素是否被访问数组
int arr1[100] = { -1 };//奇数
int arr2[100] = { -1 };//偶数
bool map[100][100] = { false };//map[i][j]表示奇数的第i个与偶数的第j个组合成素数
int match[100];//match[j]表示第j个偶数的匹配的奇数bool isPrime(int num)
{int data = num;int loopNum = sqrt(num);for (int i = 2; i <= loopNum; i++){if (data % i == 0){return 0;}}return 1;if (num < 4)return num > 1;//任何的数可以划分为  6i 6i+1 6i+2 6i+3 6i+4 6i+5   //可以看到只能是6i+1 6i+5可能是素数if (num % 6 != 1 && num % 6 != 5)return false;//接下来验证 从i=2到i=sqrt(Num)是否能被Num整除//可以排除 被除数为   6i+2 6i+3 6i+4 6i   反证:如果num能被6i整除  那么num是6的倍数  与num=6i+1或6i+5不符合for (int i = 5; i < sqrt(num); i += 6){if (num % (i) == 0 || num % (i + 2) == 0)return false;}return true;}bool find(int u)  //奇数第u个寻找偶数匹配  组配成素数组合
{for (int v = 0; v < N; v++){//u v可以组配成素数组合  并且本轮寻找增广路  v还没被讨论(每一轮都要初始化)if (map[u][v] && !visit[v]){visit[v] = true;//若v点还没有匹配(=-1)  或者能够将匹配任务推卸给 v之前的匹配对象if (match[v] == -1 || find(match[v])){match[v] = u;  //构建新的匹配return true;}}}return false;}int main()
{int num;cin >> num;for (int i = 0; i < num; i++){int tmp;cin >> tmp;if ((tmp & 1) == 1)  //奇数{arr1[M++] = tmp;}else  //偶数{arr2[N++] = tmp;}}for (int i = 0; i < M; i++)for (int j = 0; j < N; j++){if (isPrime(arr1[i] + arr2[j]))  //和为素数map[i][j] = true;//标记为能匹配}for (int i = 0; i < N; i++)match[i] = -1;int count = 0;for (int i = 0; i < M; i++){//注意每一轮研究 奇数的第i个能否完成匹配,//每一轮研究前  都需要将偶数的每个数初始化为 没有讨论visit=false;for (int j = 0; j < N; j++)visit[j] = false;if (find(i))count++;}cout << count << endl;
}

二分图的最大匹配-解决匹配问题相关推荐

  1. 二分图的最大匹配 匈牙利算法

    基本概念 1.二分图: 二分图又称作二部图,是图论中的一种特殊模型. 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别 ...

  2. 用匈牙利算法求二分图的最大匹配

    转载大神的!! 什么是二分图,什么是二分图的最大匹配,这些定义我就不讲了,网上随便都找得到.二分图的最大匹配有两种求法,第一种是最大流(我在此假设读者已有网络流的知识):第二种就是我现在要讲的匈牙利算 ...

  3. CF 277.5 B.BerSU Ball 二分图的最大匹配 模版题

    题意:求二分图的最大匹配数量 模版如下: //二分图匹配(匈牙利算法的DFS实现) //初始化:g[][]两边顶点的划分情况 //建立g[i][j]表示i->j的有向边就可以了,是左边向右边的匹 ...

  4. DAG的最小路径覆盖和二分图的最大匹配

    DAG的最小路径覆盖和二分图的最大匹配 DAG的最小路径覆盖是指找最小数目的互相不相交的有向路径,满足DAG的所有顶点都被覆盖. 首先给出公式:DAG的最小路径覆盖数=DAG图中的节点数-相应二分图中 ...

  5. POJ 2584 T-Shirt Gumbo (二分图多重最大匹配)

    题意 现在要将5种型号的衣服分发给n个参赛者,然后给出每个参赛者所需要的衣服的尺码的大小范围,在该尺码范围内的衣服该选手可以接受,再给出这5种型号衣服各自的数量,问是否存在一种分配方案使得每个选手都能 ...

  6. 二分图的最大匹配问题

    1.定义 二分图:   将一个图的所有顶点划分为两个不相交集U和V,使得图中的每一条边的顶点分别属于点集合U和点集V,即同一点集中的点不构成边,这样的图叫做二分图.维基百科中给出的无向图G的二分图的充 ...

  7. 【网络流】基础二分图的最大匹配问题

    标题已经说了,所以有基础的牛们就别想在这篇博客里看到什么有价值的东西.这里的解法是最基础的. 二分图是一种可以把图中的点分为两个不同的集合,且同一集合中不存在任意两个有边联通的点(就算是单向边也不行) ...

  8. Jamie's Contact Groups ——(一对多)二分图多重最大匹配

    题目链接:http://poj.org/problem?id=2289 Jamie is a very popular girl and has quite a lot of friends, so ...

  9. 861. 二分图的最大匹配

    861. 二分图的最大匹配 给定一个二分图,其中左半部包含 n1 个点(编号 1∼n1),右半部包含 n2 个点(编号 1∼n2),二分图共包含 m 条边. 数据保证任意一条边的两个端点都不可能在同一 ...

最新文章

  1. javascript 数组和对象的浅复制和深度复制 assign/slice/concat/JSON.parse(JSON.stringify())...
  2. 迎来“进化”风口的电竞,能否在未来站上传统体育的肩膀?
  3. 文字描边_6招迅速做出炫酷PPT字效!|10分钟干货第二期(文字描边)
  4. 无限极分类php简单,创建无限极分类树型结构的简单方法
  5. JAVA基础----简答
  6. 实测!让M1芯片版mac也能写入NTFS格式磁盘,完整教程附上!
  7. Java程序员技术栈
  8. 【狂神说Java】Docker最新超详细版教程通俗易懂笔记
  9. window.open打开txt文件
  10. 从零开始编写minecraft光影包(1)基础阴影绘制
  11. 肤色冷暖色测试软件_认识你自己|肤色冷暖测试
  12. mysql to sqlserver_mysql to sqlserver
  13. 苹果内购后台(java)验证订单
  14. 论文分享 | 可控笔触的快速风格化迁移
  15. ble 读写特征值特征值_BLE添加特征值
  16. 变位词算法C语言,程序实现英语变位词的搜索算法
  17. 纪念一位大师中的大师
  18. c语言printf( aaa ),有大佬知道那里错了吗。aaa=jia();那报错了#incl
  19. 如何理解行高和高度?
  20. MyCat是什么?为什么要用MyCat?

热门文章

  1. 聊一下盲盒app开发 盲盒小程序开发
  2. 浅析swift optional
  3. 一、旋转矩阵,旋转向量,单位四元数的相互转换总结
  4. jQuery过滤器:筛选jquery对象数组中的DOM对象
  5. win32程序窗口的创建
  6. 通过keil使用汇编语言生成二进制文件,并使用vivado仿真cortexm0处理器
  7. HDU 6194 后缀数组+单调栈
  8. python的split()函数!
  9. win10专业版没有触摸板选项_Windows10触控板的正确使用方法
  10. rabbitmq用户及vhost配置