笔记——洛谷P1014Cantor表
这道题有一个主要的坑点是z型排序是什么东西。所谓z字型排序,就是指对应一个正方形表格,以斜行为单位,依次遍历,其方向为从左上角开始,向右上,向左下,向右上,向左下(如图所示)。
值得一提的是:最左上角的(1,1)方格作为起点是没有方向的(它所在的斜行仅有它自己),我是为了方便理解才描述成向右上的。
依照上图描述的排序方式,每格的编号如下:
此外,考虑到题中给的cantor数表是三角形的,我们可以把图再裁剪掉一半,就是这样:
把这张“编号图”重叠在数表上就可以清晰的看出每个数对应的编号了。
接下来开始研究每个位置的数的规律。他们具有以下规律:
x在第一行的奇数个时(i=1,j mod 2=1):a_x=(j^2-j)/2+j;
x在第一行的偶数个时(i=1,j mod 2=0):a_x=(j^2-j)/2+1;
x在第一列的奇数个时(j=1,i mod 2=1):a_x=(i^2-i)/2+1;
x在第一列的偶数个时(j=1,i mod 2=0):a_x=(i^2-i)/2+i;
x不在第1行或第1列,且在偶数斜行时((i+j) mod 2=1):该斜行最靠上(右)的数-两位置的横向距离(abs(j-j’));
x不在第1行或第1列,且在奇数斜行时((i+j) mod 2=0):该斜行最靠下(左)的数+两位置的横向距离(abs(j-j’));
根据以上规律可以计算所有位置的编号了。
此外,每一点上的数本身也是有规律的,这个规律比较好找,就是这一点的横坐标除以纵坐标(i/j);
最后就是查找输出了,虽然也可以直接暴力(我大概计算了一下,大概的范围是1<=i,j<=640),但是我才去了另一种方法,就是先缩小范围至某一斜行,再对该斜行进行遍历。
最后附上AC代码:
#include<iostream>
using namespace std;
int f(int i,int j)
{
int ans=0;
if (i==1&&j%2==1)
{
ans=(j*j-j)/2+j;
}
else if (i==1&&j%2==0)
{
ans=(j*j-j)/2+1;
}
else if (j==1&&i%2==1)
{
ans=(i*i-i)/2+1;
}
else if (j==1&&i%2==0)
{
ans=(i*i-i)/2+i;
}
else if ((i+j)%2==1)
{
int tmp=j;
while (i!=1)
{
i--;
j++;
}
if (tmp-j>0)
{
ans=f(i,j)+(tmp-j);
}
else
{
ans=f(i,j)+(j-tmp);
}
}
else if ((i+j)%2==0)
{
int tmp=i;
while (j!=1)
{
i++;
j--;
}
if (i-tmp>0)
{
ans=f(i,j)+(i-tmp);
}
else
{
ans=f(i,j)+(tmp-i);
}
}
return ans;
}
int main()
{
int n,m=0;
int a;
int i,j;
cin>>n;
for (a=1;m<n;a++)
{
m+=a;
}
a=a-1;
for (i=a,j=1;j<=a;i--,j++)
{
if (f(i,j)==n)
{
cout<<i<<"/"<<j<<endl;
break;
}
}
return 0;
}
-END-