CodeForces 449C Jzzhu and Apples

题目链接

(贪心+构造)

题意:

         给出一个n,问n以内最多能构成多少组最大公约数>1的数,并输出它们。

题解:

         对于最大公约数>1的任意2个数来说分为奇数和偶数的情况,奇数可以匹配它的倍数(可能是奇数也可能是偶数),然而偶数只能匹配偶数,那么我们为了能够得到更多的组合可以先对【3,n】的所有素数进行倍增(把尽可能多的奇数匹配掉),统计没有被访问过的倍增的总数,若是奇数则删去其中一个偶数(对于这个偶数,我们可以让他跟等下偶数匹配的任意偶数成对),剩下的两两进行组合即可,然后在总的对n以内的所有没有被访问过的偶数进行匹配即可。(至于为什么是对素数进行倍增呢,因为一个素数只可能和其倍数有大于1的最大公约数,否则他就被抛弃了,为了尽可能的多组配对情况,应该尽可能把最难匹配的先尽可能的匹配了,从而达到贪心的最优策略)(二分图卡不过去,亲测...只能从贪心的思想去构造)

代码如下:

#include<iostream>
#include<cmath>
#include<string>
#include<cstring>
#include<cstdio>
#include<time.h>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
const int maxn = 1e5 + 500;
int vis[maxn];
int pri[maxn];
int mx[maxn], my[maxn];
vector<int>s;
void fun() {//O(nlogn)素数筛
	int t = 0;
	for (int i = 2; i <= (int)1e5+500; i++) 
		if (!vis[i]) {
			pri[t++] = i;
			for (int j = i + i; j <= (int)1e5; j += i)
				vis[j] = true;
		}
}
int main()
{
	fun();
	int n, top, cnt;
	while (~scanf("%d", &n)) {	
		top = 0; 
		memset(vis, 0, sizeof(vis));	
		for (int i = 1; pri[i] <= n; i++) {//先将[3,n]内的素数翻倍去匹配,把尽可能多的奇数匹配掉
			s.clear();
			int x = pri[i];         
			for (int j = x; j <= n; j += x)
				if (!vis[j]) s.push_back(j);
			cnt = s.size();
			if (cnt % 2) {//若是存在奇数个的情况为了后面的更优策略,把其中的一个偶数取出来
				for(int j=0;j<cnt;j++)
					if (s[j] % 2 == 0) {
						s.erase(s.begin() + j);
						break;
					}
			}
			cnt = s.size();
			for (int j = 1; j < cnt; j += 2) {
				vis[s[j - 1]] = 1, vis[s[j]] = 1;
				mx[top] = s[j - 1], my[top++] = s[j];
			}		
		}
		s.clear();
		for (int i = 2; i <= n; i += 2) //将剩下的所有偶数进行两两配对
			if (!vis[i])
				s.push_back(i);
		cnt = s.size();
		for (int i = 1; i < cnt; i += 2)
			mx[top] = s[i - 1], my[top++] = s[i];
		cout << top << endl;
		for (int i = 0; i < top; i++)
			printf("%d %d\n", mx[i], my[i]);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_41156591/article/details/80469209