(贪心+构造)
题意:
给出一个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; }