基础算法题——Harder Gcd Problem(数论、思维)

你的名字 2023-03-01 09:40 18阅读 0赞
题目

题目链接
给定一个 n,将 2~n 内的数进行一对一匹配,每个数仅能利用一次。
假设 a 与 b 匹配,则 gcd(a,b) != 1。现求 2~n 内最大匹配数量,并输出匹配数对。

输入
T代表输入组数。
下面T行,每一行一个数字n。

输出
输出最大匹配数量 m
下面m行,每一行一对匹配数对

示例1
输入样例
2
4
10
输出样例
1
2 4
4
3 9
5 10
8 2
4 6


解题思路

数论知识:合数一定能够通过质数相乘得到。

总体思路

  • 将 n 以内的质数尽量匹配。
  • 通过质数将合数尽量匹配。

逐步分析
①、筛选出质数存储在数组 p 中。

②、在 p 中寻找到最大小于 n/2 的质数下标 pos。

③、从大到小枚举 p 数组中下标 pos~0 的数。
(质数越大越难匹配成功,将难匹配成功的质数先匹配)

思考

我们知道一定存在 2 * p[i] <= n ,但我们并不知道,在 n 内是 p[i] 的倍数数量 x 是多少 ?
(ps:假设p[i]=3,那么在10内 x = 3,分别为3,6,9)

为了方便数字匹配,我们要提前保留一个2 * p[i]。若 x 为偶数,那么则将 2 * p[i] 这个数加入匹配,否则则舍弃 2 * p[i]。

④、枚举完 n/2 以内所有质数,在合数一定能够通过质数相乘得到的条件下,直接输出结果。


实现代码

  1. #include<bits/stdc++.h>
  2. #define ll long long;
  3. using namespace std;
  4. const int N=2e5+5;
  5. int p[N], ans[N], tot, cnt;
  6. bool judge[N], vis[N];
  7. void init(){
  8. int n=2e5;
  9. for(int i=2;i<=n;i++){
  10. if(judge[i]==false){
  11. p[cnt++]=i; //存储质数
  12. for(int j=2; j*i<=n;j++){
  13. judge[i*j] = true;
  14. }
  15. }
  16. }
  17. }
  18. int main(){
  19. int t;
  20. init();
  21. scanf("%d",&t);
  22. while(t--){
  23. int n;
  24. tot=0;
  25. scanf("%d",&n);
  26. memset(vis, false, sizeof(vis));
  27. //找到最大符合条件的质数下标
  28. int pos = upper_bound(p, p+cnt, n/2) - p - 1;
  29. for(int i=pos; i>=0; i--){
  30. //将 p[i] 的倍数的数进行匹配
  31. for(int j=p[i]; j<=n; j+=p[i]){
  32. //若 j 已经匹配过则跳过 或 j==2*p[i]
  33. if(vis[j]==true||j==p[i]*2){
  34. continue;
  35. }
  36. ans[++tot] = j;
  37. vis[j] = true;
  38. }
  39. //若p[i]倍数匹配完,tot为奇数,还差一个配对
  40. //对应的数直接取2*p[i]
  41. if(tot%2){
  42. vis[p[i]*2] = true;
  43. ans[++tot] = p[i]*2;
  44. }
  45. }
  46. printf("%d\n",tot/2);
  47. for(int i=1;i<=tot;i+=2){
  48. printf("%d %d\n", ans[i], ans[i+1]);
  49. }
  50. }
  51. return 0;
  52. }

如果文章对你有帮助,请给我个赞吧:)

发表评论

表情:
评论列表 (有 0 条评论,18人围观)

还没有评论,来说两句吧...

相关阅读