分析

对于这类问题,我们先观察是否有什么性质。由于我们要求 的所有数最后都可以到达 。而 在行动时候会经过 的所有节点。那么我们的问题就随之转换为, 最少要经过几个区间。那么我们可以先考虑 。令 为考虑到前 个机器,当前点为 的最小步数。那么我们就有两个转移 。那么这样我们的总复杂度为

  • 但其实我们仔细思考一下,可以发现,我们是否可以从 走到 只与最大值,也就是区间的右端点 有关。所以我们改变一下方程 只在 时才转移。那么这样是不会失去最优答案的,因为对于每个区间,只有我尽量靠右,才可能成为最优值。那么这样我们的时间复杂度为 了。而且观察到 只会从 转移,所以可以考虑滚动数组。这样我们的空间就为 了。

  • 我们发现,我们的瓶颈其实是出现在求解 的最小值的。那么是否有一种工具可以修改单点,而且可以查询区间最小值呢?当然我们可以使用线段树来维护,这样总的复杂度就为

  • 这题应该是一道非常经典的线段树优化 的题目了,如果有什么不太明白的,欢迎私聊。

    代码

    因群众要求,我把 的代码发一下,加深理解。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N = 1e3 + 100;
    int f[N][N],s[N],t[N],n,m;
    int main() {
      scanf("%d%d",&n,&m);
      for(int i = 1;i <= m;i++) scanf("%d%d",&s[i],&t[i]);
      memset(f,0x3f,sizeof(f));f[0][1] = 0;
      for(int i = 1;i <= m;i++) {
          for(int j = 1;j <= n;j++) {
              f[i][j] = f[i-1][j];
              if(j == t[i]) {
                  for(int k = s[i];k <= t[i];k++) 
                  f[i][j] = min(f[i][j],f[i-1][k] + 1);
              }
          }
      }
      cout << f[m][n] << endl;
      return 0;
    }

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N = 4e6 + 100,inf = 0x3f3f3f3f;
    int S[N],T[N],n,m,t[N];
    void update(int u,int l,int r,int pos,int k) {
    
      if(l == r) {t[u] = min(k,t[u]);return;}
      int mid = l + r >> 1;
      if(pos <= mid) update(u<<1,l,mid,pos,k);
      else update(u<<1|1,mid+1,r,pos,k);
      t[u] = min(t[u<<1],t[u<<1|1]);
    }
    int ask(int u,int l,int r,int L,int R) {
      if(l > R || r < L) return inf;
      if(L <= l && r <= R) return t[u];
      int mid = l + r >> 1;
      return min(ask(u<<1,l,mid,L,R),ask(u<<1|1,mid+1,r,L,R));
    }
    int main() {
      scanf("%d%d",&n,&m);
      for(int i = 1;i <= m;i++) scanf("%d%d",&S[i],&T[i]);memset(t,0x3f,sizeof(t));
      update(1,1,n,1,0);
      for(int i = 1;i <= m;i++) {
          int ans = ask(1,1,n,S[i],T[i]);
          update(1,1,n,T[i],1 + ans);
      }
      printf("%d\n",ask(1,1,n,n,n));
      return 0;
    }