题目描述:(二分图最大匹配问题)
若两个正整数的和为素数,则这两个正整数称之为“素数伴侣”,如2和5、6和13,它们能应用于通信加密。现在密码学会请你设计一个程序,从已有的N(N为偶数)个正整数中挑选出若干对组成“素数伴侣”,挑选方案多种多样,例如有4个正整数:2,5,6,13,如果将5和6分为一组中只能得到一组“素数伴侣”,而将2和5、6和13编组将得到两组“素数伴侣”,能组成“素数伴侣”最多的方案称为“最佳方案”,当然密码学会希望你寻找出“最佳方案”。
输入描述:
有一个正偶数N(N≤100),表示待挑选的自然数的个数。后面给出N个具体的数字,范围为[2,30000]。
4 2 5 6 13
输出描述:
求得的“最佳方案”组成“素数伴侣”的对数。
2
二分图最大匹配问题:
1.问题引出
通过数代人的努力,你终于赶上了剩男剩女的大潮,假设你是一位光荣的新世纪媒人,在你的手上有N个剩男,M个剩女,每个人都可能对多名异性有好感。
如果一对男女互有好感,那么你就可以把这一对撮合在一起,你拥有的大概就是下面这样一张关系图,每一条连线都表示互有好感。并且现在的要求是,尽可能撮合更多对新人。
2. 尝试进行解决
find(x); //表示当前对x号男生进行寻偶 line[i][j]; //表示 i 号男生和 j 号女生是否有好感 girl[j]; //表示 j 号女生的配偶编号 used[j]; //在当前挑选过程中,j号女生被used[j]号男生宣布了主权 line_num++; //x号男生牵手成功 伪代码: used[] 为全局变量,因为发生递归时候要共用 girl[] 为全局变量,因为发生递归时候要共用 line[][] 为全局变量,因为发生递归时候要共用 for(int i=0; i<M; i++) //遍历所有男生 { memset(used,0,sieof(used)); //处理i号男生时候0个女生被宣布主权 find(i); } bool find(int x) { for(int j=0; j<N; j++) { if( line[x][j]==1&&used[j]==0 ) //互有好感且女生没有被宣布主权 { used[j] = x; // x号男生对 j 号女生宣布主权 if( gir[j]==0 ) //如果名花无主 注意!!!名花无主和名花有主是可以合并在一起的,这里分开写只是为了注释方便 { girl[j] = x; // x号男生的配偶是 j 号女生 return true; //找到了就结束 }else{ // 名花有主的情况下,看能不能让这个主腾一下位置 if( find(girl[j]) ){ girl[j] = x; // x号男生的配偶是 j 号女生 return true; //找到了就结束 } } } } return false; //表示x不能再找到新的女生 }
下面给出前两步的详细执行过程:
顺序遍历每个男生,假设当前是 1 号男生
①顺序遍历所有女生尝试给1号男生找女朋友( find(1) ),(此时1号男生没有对任何一个女生宣布主权( used[]={0} ))
a)发现1号女生没有被宣布主权
b)发现和一号女生互有好感 line(1,1)==true
c)宣布自己对1号女生的主权 used[1]=true
d)发现1号女生无主( girl[1]==-1 )get,将1号女生配偶编号改变( girl[1]=1 ),对数加1(蓝线)line_num++; find(1)返回true;
e)返回false
注意到:女生没有被其他人宣布主权,互有好感&&女生无主 才可以get,line_num++
②顺序遍历所有女生尝试给2号男生找女朋友 ( find(2) ),(此时每个男生都没有对任何一个女生宣布主权( used[]={0} ))
a)发现1号女生没有被人宣布主权
b)发现2号男生和1号女生互有好感 line(2,1)==true
c)宣布2号男生对1号女生的主权 used[1]=true
d)但是1号女生有主,无法get( 无法令girl[1]=2 ),,此时girl[1]=1,调用find(1),看是否可以为1号男生找到一个新的女生
如果 find(1)为true,那么get,令girl[1]=2,find(2)返回ture
e)返回false
此时让一号女生原来的男朋友即1号男生去寻找新的女朋友即( find(1) )此时used中已经存在了一个被宣布主权的( used[1]==2 )(注意此时1号男生的find是嵌套在2号男生的find内部(发生了递归),并且2号男生和1号男生公用一个used[],这个used是目前只有2号男生有一个宣布主权的对象,这就保证了,1号男生不会选择当前已经被2号男生宣布了主权的1号女生)
我们来观察find(1),
a) 1号男1号女生已经被宣布主权 used[1]==true,那么 1号男生要跳过1号女生去对2号女生进行尝试
b)发现和2号女生互有好感 line(1,2)==true
c)宣布1号男生对2号女生的主权 used[2]=true
d)2号女生无主( girl[2]==-1 )get,将1号女生配偶编号改变( girl[2]=1 ),对数加1(蓝线)line_num++; find(1)返回true;
e)返回false
做题思路:
1. 首先明确只有一个偶数和一个奇数相加才会产生一个素数。因此我们可以将输入的偶数都当做男生,将输入的奇数都当做 女生
2. 是否有好感的判定就是用一个 isPrime函数即可,这个多种方法,很好搞定
3. 建立好感二维矩阵,然后就完事儿了(千万不要建立好感矩阵,直接判断两个数是否有好感,否则会内存超限)
AC代码:
#include<iostream> #include<vector> #include<cmath> #include<cstring> using namespace std; vector<int> even(1); //存放输入的偶数 vector<int> odd(1); //存放输入的奇数 int used[100]; int girl[100]; bool isPrime(int num); int isFind(int x); int main() { int n,num; while( cin>>n ){ for(int i=1; i<=n; i++){ cin >> num; if( num%2==0 ){ even.push_back(num); }else{ odd.push_back(num); } } int line_num=0; memset(girl,0,sizeof(girl)); for(int i=1; i<even.size(); i++){ memset(used,0,sizeof(used)); line_num+=isFind(even[i]); } cout<<line_num<<endl; odd.clear(); odd.push_back(1); even.clear(); even.push_back(1); } return 0; } bool isPrime(int num) { if( num==1||num==2 ){ return true; } if( num%6!=1&&num%6!=5 ){ //不在6倍数的两边的一定不是 return false; } int temp=sqrt(num); for(int i=5; i<=temp; i+=6){ //在6倍数的两边也不一定是 if( num%i==0||num%(i+2)==0 ){ return false; } } return true; } int isFind(int x) { for(int j=1; j<odd.size(); j++){ if( used[j]==0&&isPrime(x+odd[j]) ){ used[j] = x; if( girl[j]==0||isFind(girl[j]) ){ girl[j] = x; return 1; } } } return 0; }