ZOJ 3988 Prime Set (最大匹配)

Prime Set

Time Limit: 2 Seconds      Memory Limit: 131072 KB

Given an array of integers , we say a set is a prime set of the given array, if and is prime.

BaoBao has just found an array of integers in his pocket. He would like to select at most prime set of that array to maximize the size of the union of the selected sets. That is to say, to maximize by carefully selecting and , where and is a prime set of the given array. Please help BaoBao calculate the maximum size of the union set.

Input

There are multiple test cases. The first line of the input is an integer , indicating the number of test cases. For each test case:

The first line contains two integers and (, ), their meanings are described above.

The second line contains integers (), indicating the given array.

It's guaranteed that the sum of over all test cases will not exceed .

Output

For each test case output one line containing one integer, indicating the maximum size of the union of at most prime set of the given array.

Sample Input
4
4 2
2 3 4 5
5 3
3 4 12 3 6
6 3
1 3 6 8 1 1
1 0
1
Sample Output
4
3
6
0
Hint

For the first sample test case, there are 3 prime sets: {1, 2}, {1, 4} and {2, 3}. As , we can select {1, 4} and {2, 3} to get the largest union set {1, 2, 3, 4} with a size of 4.

For the second sample test case, there are only 2 prime sets: {1, 2} and {2, 4}. As , we can select both of them to get the largest union set {1, 2, 4} with a size of 3.

For the third sample test case, there are 7 prime sets: {1, 3}, {1, 5}, {1, 6}, {2, 4}, {3, 5}, {3, 6} and {5, 6}. As , we can select {1, 3}, {2, 4} and {5, 6} to get the largest union set {1, 2, 3, 4, 5, 6} with a size of 6.





题意:
有n个数,如果两个数相加为一个素数的话,那么这两个数可以构成一个素数组(素数组里面放的是这个两个数的下标)。要求你从中取k个组,并把这k个组求并,使得最后求并后的集合里面元素的个数最大

解析:
两个可以为一组的数就可以建边。
这里组成素数有条件:一定是一奇一偶(因为除了2以外,素数一定是奇数,而只有奇+偶=奇),这样就很符合二分图的特性,奇数放一边,偶数放一边。
这里需要仔细考虑的就是1的情况,因为1+1=2,1一定首先跟其他奇数一样,去跟偶数做最大匹配(因为1 1 4 6,k=2,这组样例,1一开始就跟1匹配的话,答案就只能是3,而若1跟4,6匹配的话,就可以得到两个二元组(1,4)(1,6)),并且1一定要最后进行最大匹配,因为1若匹配了其他奇数应该匹配的偶数,而1还可以和另一个1达成匹配,那样匹配数少1,所以先让其他奇数匹配完,1看看能不能使最大匹配数更大,即使无法变大,1也可以跟另外的1匹配,使匹配数增大。
最后就是求在可以构成素数组的数中,找那些未被匹配的数,一旦k>最大匹配数,就可以把这些数加进去。

注意:这里最大匹配存图的时候一定要用VECTOR,不然会T的!!!

理解1情况的几组样例
5
4 5
1 8 3 1
4 2
1 1 6 2
6 2
1 1 6 2 1 1
4 2
1 1 4 6
3 5
1 8 3

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;

const int MAXN = 1e6+10;
const int M = 3000+100;

int a[M];
int prime[MAXN*2],cnt;   //0表示素数
vector<int> map[M];    
//int visit[M];
int f[M],n,k;
int B[M],resn;

void primeTable()
{
	cnt=0;
	memset(prime,0,sizeof(prime));
	prime[0]=prime[1]=1;
	for(int i=2;i<MAXN*2 ;i++)
	{
		if(!prime[i])
		{
			for(int j=i+i;j<MAXN*2 ;j+=i)
				prime[j]=1;
		}
	}
}

int Hungary(int x)
{
	//f[x]=1;
	for(int i=0;i<map[x].size();i++)   //!!存图用vector,原因用矩阵存每次要遍历n个点,时间复杂度太大了
	{
		int u=map[x][i];
		if(f[u]==0)
		{
			f[u]=1;
			if(B[u]==-1||Hungary(B[u]))
			{
				B[u]=x;
				B[x]=u;
				return 1;
			}
		}
	}
	return 0;
}

int cmp(int a,int b)
{
	return a>b;
}

int main()
{
	int t,nonev;
	scanf("%d",&t);
	
	primeTable();
	while(t--)
	{
		int num=0;
		nonev=0;
		scanf("%d%d",&n,&k);
		for(int i=0;i<n;i++)
		{
			scanf("%d",&a[i]);
			B[i]=-2;
			map[i].clear();
		}
		sort(a,a+n,cmp);

		for(int i=0;i<n;i++)   //建立二分图
		{
			for(int j=i+1;j<n;j++)
			{
				if(!prime[a[i]+a[j]])
				{
					B[i]=B[j]=-1;
					if(a[i]==1&&a[j]==1) continue;
					if(a[i]%2)
					map[i].push_back(j);
					else
					map[j].push_back(i);
				}
			}
		}
		int ans=0;

		for(int i=0;i<n;i++)
		{
			if(a[i]%2&&B[i]==-1)  //只对奇数且度数不为0的点进行搜索
			{
				memset(f,0,sizeof(f));
				if(Hungary(i))
				{
					ans++;
				}
			}

		}
		int single=0;

		for(int i=0;i<n;i++)
		{
			if((B[i]==-2||B[i]==-1)&&a[i]==1)   //单个的1
			{
				single++;
			}
			if(B[i]==-2)   //度数为0的点
			{
				nonev++;
			}
		}
		ans+=single/2;   //二元组个数
		resn=n-nonev-ans*2;   //剩余单元组个数
		if(ans>=k)
		{
			printf("%d\n",k*2);
		}
		else
		{
			if(k-ans>resn)
			{
				printf("%d\n",ans*2+resn);
			}
			else
			{
				printf("%d\n",ans+k);
			}
		}

	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值