舉個具體的例子,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; }