(这是我自己编的题目)这个我做出来了,在相对比较正常的数据中我的算法没有问题,但是我的算法不知道有什么问题,在数据输入等于1E+32的时候结果中的分母为0了,在大于1E+32的时候会出现负数,不知道有没有人能解释一下,或者说给出更好的解题方法
题目链接:https://www.luogu.com.cn/problem/U194217
题目的大致描述是:
现代数学的著名证明之一是 Georg Cantor 证明了有理数是可枚举的。他是用下面这一张表来证明这一命题的:
【这里有个表我弄不出来,请到链接里查看,谢谢!】
输入格式
一个整数N
数据较大的话会输入科学计数法表示的形式
如:1E+20
输出格式
表中第N项
说明/提示
数据范围:
对于30%的数据:0<N<=1E+7(10的7次方)
对于100%的数据:0<N<=1E+30(10的30次方)
时间限制:
TimeLimit = 20ms(二十毫秒)
对比大数decimal计算与int计算,int有误差;看下面的代码,都是40个零,不要用科学计数法。
import decimal as di
def Out(m):
di.getcontext().prec=200
n=(di.Decimal(-1)+di.Decimal(1+8*m).sqrt())/di.Decimal(2)
n =di.Decimal(int(di.Decimal(n)) + 1)
s = n * (n + di.Decimal(1.0)) / di.Decimal(2.0)
t = s-m+1
print('n', n)
print('s', s)
print('t', t)
if n%2==0:
return(f'{n-t+1}/{t}')
else:
return(f'{t}/{n-t+1}')
x = Out(10000000000000000000000000000000000000000)
print('result', x)
def out(m):
n=int((-1+(1+8*m)**0.5)/2)+1
s = (1+n)*n//2
t = s-int(m)+1
print('n', n)
print('s', s)
print('t', t)
if n%2==0:
return(f'{n-t+1}/{t}')
else:
return(f'{t}/{n-t+1}')
print(out(10000000000000000000000000000000000000000))
def solve(n):
# find first r that (1+r)*r/2 >= n
lo, hi = 0, n
while lo < hi:
mid = (lo+hi) >> 1
if (((1+mid)*mid) >> 1) < n:
lo = mid + 1
else:
hi = mid
# the order in the Diagonal
order = n - (((hi-1)*hi)>>1)
if hi & 1:
return f"{hi+1-order}/{order}"
else:
return f"{order}/{hi+1-order}"
import time
e15 = 1000000000000000
e20 = 100000000000000000000
t=time.time()
print(solve(7))
print(time.time()-t)
t=time.time()
print(solve(e15))
print(time.time()-t)
t=time.time()
print(solve(e20))
print(time.time()-t)
1/4
0.00020432472229003906
2235880/42485481
0.0010771751403808594
3266133124/10876002501
0.0001857280731201172
注意,输入不能是科学计数法,因为科学计数法是浮点数,转换成整数的时候会变得不相等。
比如 int(1E+23) 得到的是 99999999999999991611392,负数的出现也是同样的原因。
超过大数变化应该是编码问题,比如uft-8编码2字节,存储数据超过字节编码就发生移位就出现了0和负数。换个编码试试。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int ans = 1;//计算的是第几项
int i = 1;//i代表的是分子也就是行数
int j = 1;//j代表的是分母也就是列数
int temp = 1;//1代表遍历的方向是向上的,0代表遍历的方向是向下的
int n = scanner.nextInt();
while(n!=ans) {//如果你没有判断到最后一项的话就继续判断,一个一个枚举的判断
if(temp==1) {//如果方向向上
if(i==1) {//判断是否到了上边缘
j++;//分母加1,像右边前进一格
temp=0;//改变方向
ans++;//判断下一项
continue;//方向改变了就要退出当前循环,进行下一次的判断
}else {//没有到上边缘
i--;//分子减1
j++;//分母加1
}
}else {
if(j==1) {//判断是否到了左边缘
i++;//分子加1,像下走一格
temp=1;//改变方向
ans++;//判断下一项
continue;//方向改变了就要退出当前循环,进行下一次的判断
}else {//没有到左边缘
i++;//分子加1
j--;//分母减1
}
}
ans++;//没有抵达边缘的判断结束后,都要加1
}
System.out.println(i+"/"+j);
scanner.close();
}
}
def Out(m):
n = int(m**0.5)
m = int(m)
while (1+n)*n//2<m:
n += 1
t = (1+n)*n//2-m+1
if n%2==0:
return(f'{n-t+1}/{t}')
else:
return(f'{t}/{n-t+1}')
import time
t=time.time()
print(Out(7))
print(time.time()-t)
t=time.time()
print(Out(1E+15))
print(time.time()-t)
输出结果:
1/4
0.010000228881835938
2235880/42485481
3.6888062953948975
如时间不满足要求,更快的办法直接解方程 n*n+n-2*m=0
n=(-1+(1+8*m)**0.5)/2,负根舍去,然后取整
def Out(m):
n=int((-1+(1+8*m)**0.5)/2)+1
t = (1+n)*n//2-int(m)+1
if n%2==0:
return(f'{n-t+1}/{t}')
else:
return(f'{t}/{n-t+1}')
import time
t=time.time()
print(Out(7))
print(time.time()-t)
t=time.time()
print(Out(1E+15))
print(time.time()-t)
输出结果:
1/4
0.015600204467773438
2235880/42485481
0.015599727630615234
15毫秒满足要求了!