题目
主题 3:寻友之旅
小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)
请帮助小青通知小码,小青最快到达时间是多久?
输入: 两个整数 N 和 K
输出: 小青到小码家所需的最短时间(以分钟为单位)
解法
BFS
将直线看作树,node的子节点分别是n-1, n+1, 2*n(如果存在)。从起始点N开始BFS,直到遇到目标点K。
由于每个点最多只经过一次,所以最终复杂度为O(n)。
动态规划
- 当
n > K时,只能从后向前一步一步走,那么最短时间为n - K。 - 当
n < K时,- 若
n = K/2,只要1步。 - 若
n < K/2,选择公交。贪心原则,但是其实想不出严格的证明,假设存在<=(该步骤选择公交最佳步数)的方案,反证得n >= K/2? - 若
n > K/2, 这里如果K正好在2*n和n的中点,那么坐公交正好比走路多1步。故而动态规划公式如下:- 当
k > n/2 + n时,dp[k] = dp[k/2] + k%2 + 1. - 当
k <= n/2 + n时,dp[k] = dp[k-1] + 1.
- 当
- 若
一点点改进
- 乘2可以用位运算(不过也许某些编译器会优化这点?)
- 考虑可以后退的情况(比如
/2),那么动态规划会不太适合,因为前面的结果受到后面的影响,考虑起来比较复杂。
代码
#include<iostream>
#include<vector>
using namespace std;
const int N = 1e5 + 1;
vector<int> dis(N, -1);//N到x点的路径长
int main(){
int n, k;
cin >> n >> k;
queue<int> q;
q.push(n); //从n开始走, BFS
dis[n] = 0;
while(!q.empty()){
int cur_pos = q.front();
if(cur_pos == k) break;
q.pop();
if((cur_pos << 1) < N && dis[cur_pos << 1] == -1){
dis[cur_pos << 1] = dis[cur_pos] + 1;
q.push(cur_pos << 1);
}
if(cur_pos + 1 < N && dis[cur_pos + 1] == -1){
dis[cur_pos + 1] = dis[cur_pos] + 1;
q.push(cur_pos + 1);
}
if(cur_pos - 1 >= 1 && dis[cur_pos - 1] == -1){
dis[cur_pos - 1] = dis[cur_pos] + 1;
q.push(cur_pos - 1);
}
}
cout << dis[k] <<'\n';
return 0;
}