2017CCPC秦皇岛ZOJ 3988 Prime Set

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <map>
#include <queue>

using namespace std;

#define N 2000010
#define inf 0x3f3f3f3f
int prime[N];
int num[3005];
vector<int>g[3005];
int dx[3005];
int dy[3005];
int dis;
int vis[3005];
int vm[3005];
int um[3005];
int n, k;
int f[3005];
int d[3005];

void init()
{
	memset(num, 0, sizeof(num));
	memset(g, 0, sizeof(g));
	for (int i = 0; i < 3005; i++)
	{
		g[i].clear();
	}
	memset(vm, -1, sizeof(vm));
	memset(um, -1, sizeof(um));
	memset(f, -1, sizeof(f));
}



//模板
bool searchP()
{
	queue<int>q;
	dis = inf;
	memset(dx, -1, sizeof(dx));
	memset(dy, -1, sizeof(dy));
	for (int i = 1; i <= n; i++)
		if (um[i] == -1)
		{
			q.push(i);
			dx[i] = 0;
		}
	while (!q.empty())
	{
		int u = q.front();
		q.pop();
		if (dx[u]>dis)  break;
		for (int i = 0; i<g[u].size(); i++)
		{
			int v = g[u][i];
			if (dy[v] == -1)
			{
				dy[v] = dx[u] + 1;
				if (vm[v] == -1)  dis = dy[v];
				else
				{
					dx[vm[v]] = dy[v] + 1;
					q.push(vm[v]);
				}
			}
		}
	}
	return dis != inf;
}
bool dfs(int u)
{
	for (int i = 0; i<g[u].size(); i++)
	{
		int v = g[u][i];
		if (!vis[v] && dy[v] == dx[u] + 1)
		{
			vis[v] = 1;
			if (vm[v] != -1 && dy[v] == dis) continue;
			if (vm[v] == -1 || dfs(vm[v]))
			{
				vm[v] = u;
				um[u] = v;
				return 1;
			}
		}
	}
	return 0;
}
int maxMatch()
{
	int res = 0;
	while (searchP())
	{
		memset(vis, 0, sizeof(vis));
		for (int i = 1; i <= n; i++)
			if (um[i] == -1 && dfs(i))  res++;
	}
	return res;
}


//素数打表
void isPrime()
{
	memset(prime, 0, sizeof(prime));
	prime[0] = prime[1] = 1;
	for (int i = 2; i*i <= N; i++)
	{
		if (prime[i] == 0)
		{
			for (int j = i + i; j <= N; j += i)
			{
				prime[j] = 1;
			}
		}
	}
	return;
}

int main()
{
	int cas;
	scanf("%d", &cas);
	isPrime();
	while (cas--)
	{
		init();
		int num_1 = 0;
		int num_11 = 0;
		scanf("%d%d", &n, &k);
		for (int i = 1; i <= n; i++)
		{
			
			scanf("%d", &num[i]);
			if (num[i] == 1)
			{
				num_1++;
			}
		}
		
		//建图
		for (int i = 1; i <= n; i++)
		{

			for (int j = i + 1; j <= n; j++)
			{
				int t = num[i] + num[j];
				if (num[i] == 1 && num[j] == 1)
				{
					continue;
				}
				if (prime[t] == 0)
				{
					
					if (num[i] % 2 == 0)
					{
						g[i].push_back(j);
					}
					else
					{
						g[j].push_back(i);
					}
				}
			}
			
		}
		int ans = maxMatch();
		int ans1 = 0;
		int num_111 = 0;
		
		memset(d, 0, sizeof(d));
		//HK算法中,um[i]==-1表示二分图中的一侧没有匹配的点,vm[i]==-1表示另一侧没有匹配的点
		for (int i = 1; i<=n; i++)
		{
			if (g[i].size() != 0)
			{
				if (um[i] == -1&&d[i]==0)
				{
					ans1++;
					d[i]++;
					if (num[i] == 1)
					{
						num_111++;//图中不匹配1的个数
					}
				}
				if (um[i] != -1 && num[i] == 1&&d[i]==0)
				{
					d[i]++;
					num_11++; //图中匹配点1的个数
				}
				for (int j = 0; j<g[i].size(); j++)
				{
					if (vm[g[i][j]] == -1&&d[g[i][j]]==0)
					{
						ans1++;
						d[g[i][j]]++;
						if (num[g[i][j]] == 1)
						{
							num_111++;
						}
					}
					if (vm[g[i][j]] != -1 && num[g[i][j]] == 1&& d[g[i][j]] == 0)
					{
						num_11++;
						d[g[i][j]]++;
					}
				}
			}

		}

		//分一条边的贡献和一个点的共享两种情况
		int y = 0;
		if (num_111+num_11 == 0)
		{
			y = num_1 / 2;
			ans1 += num_1-(num_1/2*2);
		}
		else
		{
			y = num_111 / 2;
			ans1 -= y * 2;
		}
		y = ans + y;
		if (k <= y)
		{
			printf("%d\n", min(n,2*k));
		}
		else
		{
			
			printf("%d\n",2*y +min(ans1,(k-y)));
		}

	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/carrie17/article/details/83213905