题意:
给出一个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;
}