素数伴侣(牛客)

题目描述:(二分图最大匹配问题)

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

猜你喜欢

转载自blog.csdn.net/qq_40923413/article/details/106090328