当青训营遇上码上掘金
主题介绍
寻友之旅
小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走
请帮助小青通知小码,小青最快到达时间是多久?
输入: 两个整数 N 和 K
输出: 小青到小码家所需的最短时间(以分钟为单位)
思路实现
广度优先搜索BFS
这是一个比较典型的BFS问题。小青在去找小码的路上,每一个位置X都有三种选择:前进到X+1,后退到X-1,前进到X×2,所以BFS思路就很好理解了,从小青的位置n开始,每次BFS三个位置 n-1,n+1,n×2 三个点,然后判断三个点是否合法(即大于等于0,小于等于100 000),同时为了不重复计算搜索过的点,用visited记录是否搜索过的情况。如果位置合法且该位置还没有搜索过,就标记该位置,记录步数,压入队列即可;直到到达k(即visited记录位置k已搜索)
Java实现
// 小青在每个位置都有三种方式行进,保证每种方式都考虑过后选择最优,所以可以开一个队列,记录BFS遍历的“点”
Queue<Integer> q = new LinkedList<>();
// 防止重复计算同一个“点”,声明一个visited,访问过就标记为true,不再重复计算
boolean[] visited = new boolean[100001];
// time[i]表示小青到位置i所需要最短时间
int[] time = new int[100001];
public int shortestTime(int n, int k) {
if(n >= k) {
// 小青在前方,然而公交车不能后退,小青只能选择每1min步行后退
return n-k;
}
Arrays.fill(visited, false);
visited[n] = true;
time[n] = 0;
q.offer(n);
while(q.size() > 0 && visited[k] == false) {
int cur = q.peek(); // 小青当前位置cur
q.poll();
// 三种行进方式
if(cur+1 <= 100000 && cur+1 >= 0 && visited[cur+1] == false) {
visited[cur + 1] = true;
time[cur + 1] = time[cur] + 1;
q.offer(cur + 1);
}
if(cur-1 <= 100000 && cur-1 >= 0 && visited[cur-1] == false) {
visited[cur - 1] = true;
time[cur - 1] = time[cur] + 1;
q.offer(cur - 1);
}
if(cur*2 <= 100000 && cur*2 >= 0 && visited[cur*2] == false) {
visited[cur*2] = true;
time[cur*2] = time[cur] + 1;
q.offer(cur*2);
}
}
return time[k];
}
动态规划DP
动态规划三部曲
-
确定dp数组及下标的含义
dp[i]表示到位置i需要的最短时间。
-
确定递推公式
考虑恢复到先前状态有3种情况: 1.向左移一位 2.向右移一位 3.向左闪现,坐标为X/2
第3种情况只有偶数时才能合法。所以递推式要分奇偶判断:
- i为偶数: dp[i] = min(dp[i], dp[i-1]+1, dp[i/2]+1, dp[i/2+1]+2)
- i为奇数: dp[i] = min(dp[i], dp[i-1]+1, dp[(i-1)/2]+2, dp[(i+1)/2]+2)
-
dp数组初始化 初始化时不能将dp[i]初始化为+INF,要初始化为abs(n-i),即最坏情况下所需要的时间。
Java实现
int[] dp = new int[100001];
public int shortestTime(int n, int k) {
// dp数组初始化
for(int i = 0; i <= k; i++) {
dp[i] = Math.abs(n-i);
}
for(int i = n+1; i <= k; i++) {
if(i % 2 == 0)
dp[i] = Math.max(dp[i], Math.max(dp[i-1]+1, Math.max(dp[i/2]+1, dp[i/2+1]+2));
else
dp[i] = Math.max(dp[i], Math.max(dp[i-1]+1, Math.max(dp[(i-1)/2]+2, dp[i+1)/2]+2));
}
return dp[k];
}