举个具体的例子,n=26,最小素因子是2,为了不重复,咱们只需枚举小于等于n/2的素数:2,3,5,7,13。web
求这些素数的倍数(小于等于n),列出表格方便观察,以下:svg
p | p*2 | p*3 | p*4 | p*5 | p*6 | p*7 | p*8 | p*9 | p*10 | p*11 | p*12 | p*13 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 |
3 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | |||||
5 | 10 | 15 | 20 | 25 | ||||||||
7 | 14 | 21 | ||||||||||
13 | 26 |
不难发现,同一行或者同一列(列>1)的任意两个数字都不互质,能够任意配对。可是在表格中数字会有重复,因为每一个数字只能用一次,因此咱们要想一个贪心策略来选数。观察上表,能够发现第2列的全部数字都会在第1行再次出现,第2列到第13列中的数字也可能会屡次出现,而只有第1列的全部数字只会出现一次。spa
考虑每一行之间的数字互相配对的方案。
(1)因为第1行(都是偶数)比较特殊,不妨从第2行开始配对,也就是把偶数留下来最后配对。
(2)因为第1列(都是素数,只出现一次)和第2列(都是偶数,而且在第1行再次出现)比较特殊,因此每行的配对都先从第3列开始。
①若是第3列后没被用过的数字个数为奇数个,则将最后一个可行数字与第1列的数字配对。
缘由:这样就会留下第2列的数字没配对,不要紧,它们都是偶数,那么就必定在第1行出现,只要最后在第1行配对全部没被用过的偶数便可。.net
②若是第3列后没被用过的数字个数为偶数个,则将第2列的数字与第1列的数字配对。
缘由:这里就体现出了贪心策略,由于第2列的数字都在第1行再次出现,也就是说它们能够和第1行的任意数字配对,可是若是那样配对就会消耗第1行的数字,总配对数显然会少于第2列与第1列数字进行配对的方案。应该要留更多的机会让第1行数字互相配对。
(3)最后配对第1行的数字,即配对全部还没被用过的偶数。code
引用文章:传送门xml
#include<stdio.h> #include<vector> #include<string.h> #include<algorithm> using namespace std; typedef long long ll; const int N=2e5+5; int prime[N],vis[N]; vector<int>ans; void init() { memset(vis,0,sizeof(vis)); vis[1]=1;int cnt=0; for(int i=2;i<=N;i++) { if(!vis[i])//i是质数 prime[++cnt]=i; for(int j=1;j<=cnt&&i*prime[j]<=N;j++) { vis[i*prime[j]]=1; if(i%prime[j]==0)break; } } } int main() { int t,n,c; init(); scanf("%d",&t); while(t--) { scanf("%d",&n); ans.clear(); int maxn=n/2; memset(vis,0,sizeof(vis)); for(int i=2;prime[i]<=maxn;i++)//从第二个素数(第二行)开始枚举 { int p=prime[i],cnt=0; for(int j=3;j*p<=n;j++)//从第三列开始枚举 { int x=j*p; if(!vis[x]) { ans.push_back(x); vis[x]=1; cnt++; } } if(cnt%2==1)//若该行除前两列外未被选中的数为奇数个,则与第一列的数匹配 { ans.push_back(p); vis[p]=1; } else{//若该行除前两列外未被选中的数为偶数个,则第一列与第二列的数匹配 ans.push_back(p); ans.push_back(p*2); vis[p]=1; vis[2*p]=1; } } int cnt=0; for(int i=1;i<=maxn;i++)//最后处理第一行剩下的偶数对 { int x=2*i; if(!vis[x]) { ans.push_back(x); vis[x]=1; cnt++; } } if(cnt%2==1)ans.pop_back();//若剩余的偶数为奇数个则有一个是不能找到匹配的将最后一个数弹出 int m=ans.size(); printf("%d\n",m/2); for(int i=0;i<m;i+=2) printf("%d %d\n",ans[i],ans[i+1]); } return 0; }