《算法竞赛入门经典》5.41数学基础-Cantor的数表

时间:2023-01-02 14:35:26

如下数列,第一项是1/1,第二项是1/2,第三项是2/1,第四项是3/1,第五项是2/2,……。输入n,输出第n项。
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
样例输入:
3
14
7
12345
样例输出:
2/1
2/4
1/4
59/99

方法一:

 #include <stdio.h>
int main()
{
int n;
while(scanf("%d", &n) == )
{
int k = , s = ;
for(; ; )
{
s += k;
if(s >= n) //s为k条斜线上数的总和
{
if(k% == ) printf("%d/%d\n", s-n+, k-s+n); //表示输出第k斜线倒数第s-n+1项
else printf("%d/%d\n", k-s+n, s-n+); //表示输出第k斜线正数第k-(s-n+1)+1项,即k-s+n项
break;
}
k++;
}
}
return ;
}

分析:
  1.数表按斜线分类,第1条斜线有1个数,第2条斜线有2个数……第i条斜线有i个数,这样,前i条斜线一共有S(k)=1+2+3+…+k=k(k+1)/2个数;并且是一条斜线从上到下,另一条斜线从下到上交错排数。
  2.确定n所在斜线:只要找到一个最小的正整数k,使得n<=S(k),那么n就是第k条斜线上的倒数第S(k)-n+1个元素(最后一个元素是倒数第一个元素,而不是倒数第零个元素);则第k条斜线的倒数(k为奇数时,第k斜线倒序)第i个元素是i/(k+1-i),正数(k为偶数时,第k斜线正序)第i个元素是(k+1-i)/i。

方法二:

 #include <stdio.h>
#include <math.h>
int main()
{
int n;
while(scanf("%d", &n))
{
int k = (int)floor((sqrt(8.0*n+)-) / -1e-) + ; //使用floor函数避免浮点误差
int s = k*(k+) / ; //s为k条斜线上数的总和
if(k% == ) printf("%d/%d\n", s-n+, k-s+n); //表示输出第k斜线倒数第s-n+1项
else printf("%d/%d\n", k-s+n, s-n+); //表示输出第k斜线正数第k-(s-n+1)+1项,即k-s+n项
}
return ;
}

分析:
  1.利用代数,n<=k*(k+1)/2,k*k+k-2n>=0,(k-(sqrt(8.0*n+1)-1)/2)*(k-(-sqrt(8.0*n+1)-1)/2)>=0,注意到k-(-sqrt(8.0*n+1)-1)/2总是正数,则k>=(sqrt(8.0*n+1)-1)/2;换言之,可以直接求出则k=(sqrt(8.0*n+1)-1)/2。
  2.floor()函数,其功能是“向下取整”,或者说“向下舍入”,即取不大于x的最大整数(与“四舍五入”不同,下取整是直接去掉小数部分);
  3.指数形式:由于计算机输入或输出时,无法表示上角或下角,规定以字母e或E代表以10为底的指数;注意:e或E之前必须有数字,e或E后面必须是带符号的十进制整数,长度最大为3位,正数时可不写+号。

方法三:

 #include <stdio.h>
int main()
{
int n;
while (scanf("%d", &n) == )
{
int k = ;
while (k < n) //k为第n项所在行数,循环的意义为所求数在第k条斜线第n项
{
n -= k; //n减去第k条斜线上的数
k++;
}
if (k% == ) printf("%d/%d\n", k+-n, n); //表示输出第k斜线倒数第(k-n)+1项
else printf("%d/%d\n", n, k+-n); //表示输出第k斜线正数第n项
}
return ;
}

分析:

  用n减去前k条斜线上的个数后再和第k条斜线上个数相比即得所求数位置。