系列文章目录
这个题对我来说是有点难的,看了一下平台上的通过率,通过率只有31%
一、Consecutive Integer
题目描述:
2005年百度之星初赛有这么一道题,一个正整数有可能被表示为m(m>1)个连续正整数之和,如:
15=1+2+3+4+5
15=4+5+6
15=7+8
但现在你的任务是判断给定的整数n能否表示成连续的m(m>1)个整数之和
解答要求:
时间限制:1000ms,内存限制:100mb
输入:
输入只有一个整数n(1<n<2^30+1)
输出:
若n能表示成连续的m个正整数之和则输出'YES',否则输出‘NO’
输入样例:
15
输出样例:
YES
二、代码详情
先上代码,再说解题思路
def func():
n=int(input())
flag=False
for i in range(1,n):
res=i
for j in range(i+1,n):
res+=j
if res==n:
flag=True
print('YES')
break
if flag:
break
if not flag:
print('NO')
if __name__ =='__main__':
func()
'''
输入:15
输出:YES
'''
三、解题思路
首先先说结论,上面的代码时可以运行出结果,而且结果也是符合预期的,但是提交代码的时候并没有通过,因为上述代码的运行时间3997.3ms,超过了题目的要求。接下来说一下思路:
题目中要求是连续的几个正整数,所以,我第一个想法就是两层循环:
第一层循环从1开始
第二层循环从2开始,然后记录1+2的值,接下来继续执行第二层循环,此时第二层循环到3,然后记录1+2+3的值……一次类推,如果第一层循环从1开始到遍历结束没有符合的结果,跳出内层循环
此时,第一层循环从2开始
第二层循环从3开始,记录2+3的值……然后执行内层循环计算2+3+4+……
……
但是还有一个问题,我需要做到满足条件的时候随时终止循环,于是我添加了一个flag标签,初始的时候赋值为False,只要满足条件就赋值为True,用来终止循环并通过最后的flag值判断是否打印NO。
四、另辟蹊径
虽然上面的程序也算解决了问题,但是提交代码的时候提示超市,未通过,所以只能换一个想法来解决,我曾设想减少循环的次数,但是盘算了一下,就算减少了可能时间还是超出限制,所以决定换一个思路来解决这个问题。
1、问题分析
首先,如果这个数字N是奇数的话,肯定是满足条件的,奇数总是可以表示成两个连续整数的和
接下来需要考虑偶数的情况:
反证:假设这个偶数可以被换成连续的自然数相加的形式,那么可以表示为:
用等差数列求和公式,求出
若k+1为偶数,则2x+k为奇数,若k+1为奇数,则2x+k为偶数,所以等号的左边必定是一个奇数一个偶数。将公式转换为
因为2n是偶数,左边(2x+k)(k+1)一定有一个数值是偶数。
我们将等式的两边不断地提取公因子2,如果等号的右边可以化成2^m,也就是2的幂,而等式的左边因为存在一个奇数而不能化成2^m,所以式子不可能相等,所以2的幂是不能转换成连续自然数相加的形式
2、伪代码
经过上面的推理,最终问题就简单了。伪代码如下:
输入正整数n;
if n是奇数:
print('YES')
elif: n是2的幂:
print('YES')
else:
print('NO')
3、另一个难题
那么这里就迎来了另一个问题,怎么判断一个数字是不是2的幂?
这里有一个非常巧妙的方法,就是位与运算,首先我们看一下2的幂转在二进制下具有什么特征?
print(bin(2))
print(bin(4))
print(bin(8))
print(bin(16))
'''
0b10
0b100
0b1000
0b10000
'''
都是1后面很多个0,如果将2的幂减去1,转化为二进制是很多个1(比2的幂少一位),如下
print(bin(1))
print(bin(3))
print(bin(7))
print(bin(15))
'''
0b1
0b11
0b111
0b1111
'''
两者与位与运算后得到的结果毫无疑问都是False(位与运算可以在数学啥鞥理解为且的运算,用符号&表示,0&1=0,0&0=0,1&1=1)
于是判断一个数字是否为2的幂,就可以简单写为:
if n&(n-1)==0:
print('n是2的幂')
else:
print('n不是2的幂')
五、最终版代码
def func():
n=int(input())
if n%2==1:
print('YES')
elif n&(n-1)==0:
print('YES')
else:
print('NO')
if __name__=='__main__':
func()
代码用时:12.7ms