• 洛谷千题详解 | P1014 [NOIP1999 普及组] Cantor 表【C++、Java语言】


    博主主页:Yu·仙笙

    专栏地址:洛谷千题详解

    目录

    题目描述

    输入格式

    输出格式

    输入输出样例

    解析:

    C++源码:

    C++源码2:

    C++源码3:

    Java源码:


    ------------------------------------------------------------------------------------------------------------------------------- 

     ------------------------------------------------------------------------------------------------------------------------------- 

    题目描述

    现代数学的著名证明之一是 Georg Cantor 证明了有理数是可枚举的。他是用下面这一张表来证明这一命题的:

    1/1 , 1/2 , 1/3 , 1/4, 1/5, …

    2/1, 2/2 , 2/3, 2/4, …

    3/1 , 3/2, 3/3, …

    4/1, 4/2, …

    5/1, …

    我们以 Z 字形给上表的每一项编号。第一项是 1/1,然后是 1/2,2/1,3/1,2/2,…

     ------------------------------------------------------------------------------------------------------------------------------- 

    输入格式

    整数N(1≤N≤107)。

     ------------------------------------------------------------------------------------------------------------------------------- 

    输出格式

    表中的第 N 项。

     ------------------------------------------------------------------------------------------------------------------------------- 

    输入输出样例

    输入 #1

    7
    

    输出 #1

    1/4

     ------------------------------------------------------------------------------------------------------------------------------- 

    解析:

     update:显然这样的程序并不是最短的,评论已经有很多的dalaodalao指出了,而且时间复杂度也并不优秀,但是当时就会有一种莫名的自信 至于怎么压行,评论区也说得比较明白了,所以评论不用再提供压行思路了

    update: 首先我们观察到第ii行,第jj列的数就是i/ji/j,这是第一个要发现的。

    因为题目中要求是以Z字型编号

    我们看题目中的表是:

    1/1,1/2,1/3 ……

    2/1,2/2,2/3 ……

    Z字型编号以后(把头向左偏45度):

    第一行:1/1 (1号)

    第二行:1/2 (2号) 2/1(3号)

    第三行:1/3 (6号) 2/2(5号) 3/1(4号)

    \uparrow↑ 观察法易得每一行比上一行多1

    代码里那个while循环,就是为了通过循环枚举,判断它在编号之后的第几行,第几个位置。


    (这个优化有没有都可以AC本题,但是评论指出我的时间复杂度不够优秀,因此提一提这个优化,不愿意看的可以直接略过看下一个分割线以后的内容。)

    但其实可以直接出结论优化时间复杂度从O(n)优化到O(1),这样就要考虑到等差数列求和

    公式:S=2n(an​+a1​)​

    所以,很显然Z字型排序之后,第k行的数编号n满足:

    \frac{k(k-1)}{2} < n \le \frac{k(k+1)}{2}

     ------------------------------------------------------------------------------------------------------------------------------- 

    C++源码:

    1. #include
    2. int main()
    3. {
    4. int n;scanf("%d",&n);
    5. int t=1,ans=0;//t是表示下一次跳到下一次的距离,ans是表示第几层
    6. while(1)
    7. {
    8. if(n>t){n-=t;ans++;t++;}//printf("%d\n",ans);
    9. else if(n==t&&ans%2==0){printf("1/%d",ans+1);break;}
    10. //如果在n==t,并且为偶数层,就在第一行 第ans+1个
    11. else if(n==t&&ans%2!=0){printf("%d/1",ans+1);break;}
    12. //如果在n==t,并且为奇数层,就在第ans+1行 第一个
    13. else if(n2!=0){printf("%d/%d",ans+n-t+1,t-n+1);break;}
    14. //如果在n
    15. else if(n2==0){printf("%d/%d",t-n+1,ans+n-t+1);break;}
    16. // 如果在n
    17. }
    18. return 0;
    19. }

     ------------------------------------------------------------------------------------------------------------------------------- 

    C++源码2:

    1. #include
    2. using namespace std;
    3. int main()
    4. {
    5. int i = 0, n, s = 0, x, y;//s:按整条斜线走过的格子的末尾编号
    6. cin >> n;
    7. while(s < n)//s >= n时,说明编号为n的格子在刚才加的第i斜线之中
    8. {
    9. i++;
    10. s += i;
    11. }
    12. s -= i;//此时确定n在第i条斜线上
    13. if(i % 2 == 1)//从i/1出发向右上遍历,找编号为n的格子
    14. {
    15. x = i, y = 1, s++;
    16. while(s < n)//s == n时,x,y的值即为编号为n的格子的坐标
    17. x--, y++, s++;
    18. }
    19. else//从1/i出发向左下遍历,找编号为n的格子
    20. {
    21. x = 1, y = i, s++;
    22. while(s < n)//s == n时,x,y的值即为编号为n的格子的坐标
    23. x++, y--, s++;
    24. }
    25. cout << x << '/' << y;
    26. return 0;
    27. }

     ------------------------------------------------------------------------------------------------------------------------------- 

    C++源码3:

    1. #include
    2. using namespace std;
    3. int main()
    4. {
    5. int i = 0, n, s = 0, x, y, l;
    6. cin >> n;
    7. while(s < n)//看第n格子是否在第i斜线上
    8. {
    9. i++;
    10. s += i;
    11. }
    12. s -= i;
    13. l = n-s;//在第i斜线数几个
    14. if(i % 2 == 1)//从i/1出发
    15. {
    16. x = i+1-l;//i-x+1=l
    17. y = l;
    18. }
    19. else//从1/i出发
    20. {
    21. x = l;//x-1+1 = l
    22. y = i+1-l;//i-y+1=l
    23. }
    24. cout << x << '/' << y;
    25. return 0;
    26. }

     ------------------------------------------------------------------------------------------------------------------------------- 

    Java源码:

    1. public static void main(String[] args) {
    2. Scanner scanner = new Scanner(System.in);
    3. int position = scanner.nextInt();
    4. int sum, end = 1;
    5. while (true) {
    6. sum = (1 + end) * end / 2;
    7. if (position <= sum) break;
    8. ++ end;
    9. }
    10. sum -= position;
    11. if (end%2 == 0) { // 偶数
    12. // 大数在前
    13. System.out.println((end-sum) + "/" + (1+sum));
    14. } else { // 奇数
    15. // 大数在后
    16. System.out.println((1+sum) + "/" + (end-sum));
    17. }
    18. }

     ------------------------------------------------------------------------------------------------------------------------------- 

  • 相关阅读:
    猿创征文 | 【国产数据库实战】使用docker部署PolarDB-X云原生分布式开源数据库
    如何实现车机体验”遥遥领先”?头部玩家已经给出答案
    ue rpg学习截图(p1-p59)
    开户许可证识别 易语言代码
    go热更新配置文件
    Algorithm基础算法学习
    给浏览器开挂,超好用的全平台快捷指令工具推荐
    Tomcat概述和基础优化
    c语言中函数的介绍(血书20000字!!!!)
    用C++语言写一个可读的回调函数
  • 原文地址:https://blog.csdn.net/djfihhfs/article/details/127720708