当青训营遇上码上掘金
选题3——小青找小码
代码创作思路:动态规划
动态规划算法通常用于求解某种最优性质的问题,比如求解最大值、最小值以及数量等性质。对于该类问题,通常具有一种最优子结构的性质。 我们可以通过从集合的角度来求解该类问题,比如主题三: 求解过程:
1.考虑状态定义:
f[i]表示从起点走到点i所需要的最短时间,事实上我们有多种方式走到点i,但是往往用f[i]来表示这类方式中花费时间的最小值,容易得知f[N]=0,即在起点的时候,是不需要耗费时间的
2.考虑特殊情况
当k<=n的时候,由于我们只有一种方式往回走,即从X走到X-1,所以走到n-i所需要的时间必然是i,所以这部分是不需要进行动态规划的。
3.当k>n时,考虑集合的划分方式:
f[i]表示所有到达点i的方式,我们以最后一步的选择作为划分依据,当i为奇数时,最后一步可以从i-1到i,从i/2到i,从(i+1)/2到i,从(i-1)/2到i;当i为偶数时,可以从i-1和i/2到i,所以最终的状态转移方程如下所示
f[i]=min(f[i-1]+1,f[i/2]+1)(当i为偶数)
f[i]=min(f[i-1],min(f[(i+1)/2],f[(i-1)/2]))(当i为奇数)
值得注意的是,我们小于n的位置上的i,需要先进行预处理 即f[i] = f[i+1] + 1,这样才能保证计算f[k]的时候集合的不遗漏 一些其他思路: 这题或许也可以通过BFS(广度优先搜索)的方式来求解,当我们走到点i时,可以继续去搜索i-1、i+1和2*i这三个点,由于数据范围比较友好,且不会出现负数,所以这也不失为一种可行的好方法 具体代码如下:
参考代码如下
const int N = 100100;
int f[N];
using namespace std;
int main() {
int n,k;
cin>>n>>k;
if(k<=n)
{
cout<<n-k<<'\n';
}
else
{
for(int i=0;i<n;i++)
{
f[i] = n - i;
}
//预处理小青走到[0,n-1]的最短时间
for(int i=n+1;i<=k;i++)
{
f[i] = f[i-1] + 1;
if(i%2==0)
{
f[i] = min(f[i],f[i/2]) + 1;
}
else
{
f[i] = min(f[i/2],f[(i+1)/2]) + 2;
}
}
cout<<f[k]<<'\n';
}
return 0;
}