当青训营遇上码上掘金
看不懂证明过程可以直接阅读源码和样例分析哦~
题目:寻友之旅
小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)
请帮助小青通知小码,小青最快到达时间是多久?
输入: 两个整数 N 和 K
输出: 小青到小码家所需的最短时间(以分钟为单位)
题目可抽象为如下
(×2代表坐公交车一次,+a代表走a步(a可以小于0))K=(...((N+a0)×2+a1)×2+a2)...)×2+an ⋅⋅⋅①T=n+i=0∑n∣an∣(K,N,T,n∈N,且a0,a1,a2,...,an∈Z,且0≤N,K≤100000)(考虑实际意义(走到负数位置是没有意义的),式①中的计算过程量均应为自然数)已知K,N,求T的最小值
思路分析
(缩小ak的范围)设方程①的解为A=(a0,a1,a2,...,an)T则方程①必存在另一解A′=(a0,a1,a2,...,ak+1,ak+1−2...,an)T且方程①必存在另一解A′′=(a0,a1,a2,...,ak−1,ak+1+2...,an)T(其中k≥0)此时T−T′=∣ak∣−∣ak+1∣+∣ak+1∣−∣ak+1−2∣此时T−T′′=∣ak∣−∣ak−1∣+∣ak+1∣−∣ak+1+2∣若ak+1≥2 则T−T′一定大于0,即T′<T 则解A′比解A更优若ak+1≤−2 则T−T′′一定大于0,即T′′<T 则解A′′比解A更优综上所述若A为最优解,则∀k∈[1,n],ak∈{−1,0,1} ⋅⋅⋅②逆向推导(÷2表示曾上过一次公交车,−a表示曾走过a步)方程①可转化为N=((...(K−an)...)÷2−a2)÷2−a1)÷2−a0 ⋅⋅⋅③设数组{Kn}首项为K0=K,通项公式为Kn=(Kn−1−an)÷2则N=Kn−a0,且{Kn}∈N ({Kn}的实际意义为每次上公交车的位置)为求得最优解A,由②得∀k∈[1,n],ak∈{−1,0,1}(Kn的可能的取值个数)由Kn=(Kn−1−an)÷2、{Kn}∈Z、∀k∈[1,n],ak∈{−1,0,1}易证:若Kk−1有2个差为1的可能的取值,则Kk也有2个差为1的可能的取值易证:若Kk−1只有1个可能的取值,则Kk只有1个可能的取值 或 2个差为1的可能的取值所以∀k∈[1,n],Kk只有1个可能的取值 或 2个差为1的可能的取值所以Kn只有1个可能的取值 或 2个差为1的可能的取值(Kk的范围)∀k∈[0,n−1],当Kk=2时,∀i∈[1,n−k],Kk+i=1 (原地踏步)且Kk=2到Kk+1=1的时间花费为1,与走路相同,所以没必要坐公交车所以,对于{Kn},若Kk=2,则n=k(实际意义为,如果曾坐公交车到达2的位置,那么没必要)所以∀k∈[0,n],Kk≥2(求已知K时n的最大值)让Kn以最小的速度递减,即Kn=(Kn−1+1)÷2令Kn=2,解得n=log(K−1)所以n的最大值为⌈log(K−1)⌉综上,解题思路为(n的所有可能×Kn的所有可能=答案的所有可能)由K0=K、Kn=(Kn−1−an)÷2、{Kn}∈Z、∀k∈[1,n],ak∈{−1,0,1}逐个递推算出K0到K⌈log(K−1)⌉,若算出2则下一次结束循环并在递推计算的过程中计算T的值,保留T的历史最低记录即答案最坏时间为⌈log(K−1)⌉×2,即时间复杂度为O(logK)补充特例:if N≥K,直接T=N−Kelse if K≤1,那么只有K=1,N=0这种情况,直接T=1else 按上面的方法计算
代码展示
from math import log
N = int(input())
K = int(input())
if N >= K:
T = N - K
print(T)
elif K <= 1:
T = 1
print(T)
else:
nMax = int(log(K - 1)) + 1
K1 = [0] * nMax
K2 = [0] * nMax
T1 = [0] * nMax
T2 = [0] * nMax
K1[0] = K
T1[0] = 0
for i in range(1, nMax):
if K2[i - 1] != 0 and K1[i - 1] % 2 == 0 and K2[i - 1] % 2 != 0:
K1[i] = K1[i - 1] // 2
T1[i] = min(T1[i - 1] + 1, T2[i - 1] + 2)
K2[i] = (K2[i - 1] + 1) // 2
T2[i] = T2[i - 1] + 2
if T1[i] - T2[i] > 1:
T1[i] = T2[i] + 1
if T2[i] - T1[i] > 1:
T2[i] = T1[i] + 1
elif K2[i - 1] != 0 and K1[i - 1] % 2 != 0 and K2[i - 1] % 2 == 0:
K1[i] = (K1[i - 1] - 1) // 2
T1[i] = T1[i - 1] + 2
K2[i] = K2[i - 1] // 2
T2[i] = min(T2[i - 1] + 1, T1[i - 1] + 2)
if T1[i] - T2[i] > 1:
T1[i] = T2[i] + 1
if T2[i] - T1[i] > 1:
T2[i] = T1[i] + 1
elif K2[i - 1] == 0 and K1[i - 1] % 2 == 0:
K1[i] = K1[i - 1] // 2
T1[i] = T1[i - 1] + 1
K2[i] = T2[i] = 0
elif K2[i - 1] == 0 and K1[i - 1] % 2 != 0:
K1[i] = (K1[i - 1] - 1) // 2
T1[i] = T1[i - 1] + 2
K2[i] = (K1[i - 1] + 1) // 2
T2[i] = T2[i - 1] + 2
if T1[i] - T2[i] > 1:
T1[i] = T2[i] + 1
if T2[i] - T1[i] > 1:
T2[i] = T1[i] + 1
if K1[i] == 2 or K2[i] == 2:
break
Tmin = K - N
for i in range(1, nMax):
if K1[i] == 0:
break
Tmin = min(Tmin, abs(N - K1[i]) + T1[i])
if K2[i] != 0:
Tmin = min(Tmin, abs(N - K2[i]) + T2[i])
print(Tmin)
举例分析
N=5,K=101则
-
初始化记录K1[0]=101,T1[0]=0
-
从K1[0]=101可以到(101-1)/2=50和(101+1)/2=50,这个操作需要花费时间2
- 所有可达位置为50和51,总花费为T1[0]+2=2和T1[0]+2=2
- 记录K1[1]=50,K2[1]=51,T1[1]=2,T2[1]=2
-
从K1[1]=50可以到50/2=25,这个操作需要花费时间1,
从K1[1]=51可以到(51-1)/2=25和(51+1)/2=26,这个操作需要花费时间2
- 所有可达位置为25和26,总花费为min(T1[1]+1,T2[1]+2)=3和T2[1]+2=4
- 记录K1[2]=25,K2[2]=26,T1[2]=3,T2[2]=4
-
从K1[2]=25可以到(25-1)/2=12和(25+1)/2=13,这个操作需要花费时间2,
从K1[2]=26可以到26/2=13,这个操作需要花费时间1
- 所有可达位置为12和13,总花费为T1[2]+2=5和min(T1[2]+2,T2[2]+1)=5
- 记录K1[3]=12,K2[3]=13,T1[3]=5,T2[3]=5
-
从K1[3]=12可以到12/2=6,这个操作需要花费时间1,
从K1[3]=13可以到(13-1)/2=6和(13+1)/2=7,这个操作需要花费时间2
- 所有可达位置为12和13,总花费为min(T1[3]+1,T2[3]+2)=6和T1[3]+2=7
- 记录K1[4]=6,K2[4]=7,T1[4]=6,T2[4]=7
-
从K1[4]=6可以到6/2=3,这个操作需要花费时间1,
从K1[4]=7可以到(7-1)/2=3和(7+1)/2=4,这个操作需要花费时间2
- 所有可达位置为6和7,总花费为min(T1[4]+1,T2[4]+2)=7和T1[4]+2=9
- T2[5]-T1[5]>1,而从K1[5]=3走到K2[5]=4只需要1分钟,所以令T2[5]=8
- 记录K1[5]=3,K2[5]=4,T1[5]=7,T2[5]=8
-
从K1[5]=3可以到(3-1)/2=1和(3+1)/2=2,这个操作需要花费时间2,
从K1[5]=4可以到4/2=2,这个操作需要花费时间1
- 所有可达位置为1和2,总花费为min(T1[5]+1,T2[5]+2)=9和T1[5]+2=9
- 记录K1[6]=1,K2[6]=2,T1[6]=9,T2[6]=9
-
K2[6]==2循环结束
-
最终结果为
- K1=[101, 50, 25, 12, 6, 3, 1, 0]
- K2=[0, 51, 26, 13, 7, 4, 2, 0]
- T1=[0, 2, 3, 5, 6, 7, 9, 0]
- T2=[0, 2, 4, 5, 7, 8, 9, 0]
-
遍历求abs(K1[i]-N)+T1[i]和abs(K2[i]-N)+T2[i]的最小值
- abs(K1[i]-N)为还需要步行的距离
- T1[i]为已经花费的时间
- K2,T2同理
-
最小值结果为abs(K1[4]-N)+T1[4]=6-5+6=7
- 回推步骤分析结果
- ((((101-1)/2+0)/2-1)/2+0)/2-1=5
- 即((((((5+1)*2)+0)*2+1)+0)*2+0)*2+1=101花费7分钟