[NOIP1999 普及组] Cantor 表

[NOIP1999 普及组] Cantor 表

题目描述:

现代数学的著名证明之一是 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 <= 10^7)。

输出格式:

表中的第 N 项。

样例 #1:

样例输入 #1:

7

样例输出 #1

1/4

思路:

算法1:模拟

模拟,按题意一个个枚举

时间复杂度O(n),可以通过本题n≤10^7.

算法2:枚举

  发现Z字形的每条斜线可以快速枚举,即枚举

  1/1 , 1/2 , 3/1 , 1/4 , 5/1 , 1/6……找到要求的第n项所在斜线,再一个个枚举或计算得出答案

  时间复杂度O(√n),可以通过n≤10^14

算法2.2:枚举简化

  枚举第n项在哪一行,计算得出答案,比算法2好写,

  时间复杂度同算法2

算法3:二分

  发现第i条斜线(即分子分母之和=i+1的所有项)中包含i*(i-1)/2+1至i*(i+1)中的每一项,所以可以二分分子分母之和,再根据分子分母之和的奇偶性直接计算第n项

  时间复杂度O(㏒₂n),可以通过n≤10^18,加上高精可通过n≤10^1000

二分算法代码:

#include<iostream>
#include<cmath>
using namespace std;
int main(){
    long long l=1,r,mid,n,a;
    cin>>n;
    r=n;
    while(l<r){
        mid=(l+r)/2;
        if(mid*(mid+1)/2<n)
		  l=mid+1;
        else 
		  r=mid;
    }
    a=n-l*(l-1)/2;
    if(l%2==0)
	  cout<<a<<'/'<<l+1-a;
    else 
	  cout<<l+1-a<<'/'<<a;
    return 0;
}

当然,最简单的还是“数学”直接推,O(1)复杂度直接起飞,所以我们直接来推一下吧!

算法4:数学直接推

已知数据是求第个。

明显Z形画出来的三角,从左上到右下的行数是从1开始公差为1的等差数列。所以利用求和公式,设行数的话则有:

因此我们 设使得 

根据建立起的函数的递增性,可知

所以通过求根公式求出然后向上取整就可以在O(1)的时间复杂度求出行数了。

Which is 

接下来,还要求出所在当行的具体位置,这个就很容易了,只需要知道 到那一行总共有多少个:明显

所以要求的也就是那一行的第个。

接下来是一个对于知道行数+第几个的Cantor形式求法:

对于第a行,中所有个体,都有(“/”左边)+(“/”右边)

同时 

输出的结果应该是:

<u>(_第几个_ )/(a+1- _第几个_ )</u>

<u>而剩下的则“/”两边相反即好。</u>

以上就是O(1)(其实应该没比二分快多少,相当于让系统做了三分而已)解决此题的详解。既然是数学推算,代码就不贴了,没啥意思。 

总结:

  该题题目很简单,可以用非常简单的算法解决,但是我们可以举一反三,更加深入的思考,想到更牛X,更高效快速的算法。

题目链接:
[NOIP1999 普及组] Cantor 表 - 洛谷https://www.luogu.com.cn/problem/P1014

猜你喜欢

转载自blog.csdn.net/wo_ai_luo_/article/details/130231288