当青训营遇上码上掘金

109 阅读2分钟

当青训营遇上码上掘金

选题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;
}