当青训营遇上码上掘金
主题介绍
我选择的是主题三:寻友之旅
小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)
请帮助小青通知小码,小青最快到达时间是多久?
输入: 两个整数 N 和 K
输出: 小青到小码家所需的最短时间(以分钟为单位)
题解
根据题意分析,对于小青而言,他可以对自己当前所在的位置做“+1”,“-1”或“*2”这三种操作。 我采用动态规划的方法解决本题。
- 当N>=K时,显然可以直接返回N-K作为答案。
- 当N<K时,对每一个当前所在位置x,我们可以得出当前位置的递推公式
- 当x为奇数时,dp[x] = dp[x-1] + 1;
- 当x为偶数时,dp[x] = 1 + min(dp[x-1], dp[x/2])
并且仅会存在,当小青在坐公交时超过了小码所在位置时,小青会向回走;其余情况下均不会向回走。 因而可以给出如下代码:
package main
import "fmt"
func main() {
var N, K int
N, K = 0, 98
//fmt.Scanf("%d, %d", &N, &K)
if N >= K {
fmt.Printf("%d", N - K)
} else {
var dp [200005] int
for i := N; i >= 0; i-- {
dp[i] = N - i;
}
for i := N + 1; i <= 2 * K; i++ {
if i % 2 == 1 {
dp[i] = dp[i - 1] + 1
} else {
if dp[i - 1] < dp[i / 2] {
dp[i] = dp[i - 1] + 1
} else {
dp[i] = dp[i / 2] + 1
}
}
//fmt.Printf("%d %d\n", i, dp[i])
}
var now_min, now int
now_min, now = dp[2 * K], 2 * K
for i := 2 * K - 1; i >= K; i-- {
if (dp[i] > now_min + now - i) {
dp[i] = now_min + now - i
}
now_min, now = dp[i], i
}
fmt.Printf("%d", dp[K])
}
}
仔细观察代码,我们可以发现,可以在从N遍历到K的同时,对向回走的情况同时进行处理。
package main
import "fmt"
func min(a ...int) int {
min := 1000000
for _, i := range a {
if i < min {
min = i
}
}
return min
}
func main() {
var N, K int
N, K = 0, 100
//fmt.Scanf("%d, %d", &N, &K)
if N >= K {
fmt.Printf("%d", N - K)
} else {
var dp [200005] int
dp[K] = 1000000
for i := 0; i <= K; i++ {
if i != K {
dp[i] = 1000000
}
if i <= N {
dp[i] = min(N - i, dp[i])
} else {
if i % 2 == 1 {
dp[i] = min(dp[i - 1] + 1, dp[i])
} else {
dp[i] = min(dp[i], dp[i - 1] + 1, dp[i / 2] + 1)
}
}
num := i * 2 - K
if num < 0 {
num = num * -1
}
dp[K] = min(num + dp[i] + 1, dp[K])
}
fmt.Printf("%d", dp[K])
}
}
时间复杂度为。
下面是码上掘金的代码,如有错误欢迎指正。