uva12170 Easy Climb

我觉得这个题挺难的......
首先这个题的结论 hp+kd我没有证明出来(范围肯定没有问题,根据数量少的时候和感觉就觉得是对的,但是没有严格的证明)

dp[i][x]就是在dp[i-1][x-d---x+d]中找一个最小值转移,再加上常数|h[i]-x|,这样就可以用优先队列来优化。
虽然说是用单调队列来求每个滑动窗口的最小值,然而再深入分析一步就会发现一个规律,每次算完一个阶段后,该阶段的dp序列呈现的是一个先下降,后上升的趋势,而且下降时候的最低点一定是整个序列的最低点。这样,我们只需要一个front指针即可维护优先队列了。

先维护窗口左边界,别让指针k超出了窗口,如果x[k] < x[j] - d那么就k++ (因为x数组是从大到小已经排好序的),然后在不超出右边界x[j]+d 的前提下,如果dp[i][k+1] <= dp[i][k],那么k++;

为什么这样是对的的? 好像和之前说的单调队列一点也不一样啊! ?其实是一样的操作,仔细回想维护单调队列时是怎么操作的 :用两个指针front、rear 先更新左边界,防止他超出边界,一旦超出就front++; 然后每次新加进来一个值就要看看当前队列最右端的元素与新值的大小,如果大于新值那么就rear--,将无用的元素请出队列 ,直到小于新值,就将新值加入, 然而其实上边那个用一个指针的方法是如出一辙的,只不过将删除无用值这一步放到了求最小值时,也就是更新k时 。


unique函数属于STL中比较常用函数,它的功能是元素去重。即”删除”序列中所有相邻的重复元素(只保留一个)。此处的删除,并不是真的删除,而是指重复元素的位置被不重复的元素给占领了(详细情况,下面会讲)。由于它”删除”的是相邻的重复元素,所以在使用unique函数之前,一般都会将目标序列进行排序。返回iterator

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

typedef long long LL;

const int maxn = 100 + 5;
const int maxx = maxn*maxn*2;
const LL  INF = (1LL << 60);

LL h[maxn], x[maxx], dp[2][maxx];

int main () {
  int T;
  cin >> T;
  while(T--) {
    int n;
    LL d;
    cin >> n >> d;
    for(int i = 0; i < n; i++) cin >> h[i];
    if(abs(h[0] - h[n-1]) > (n-1)*d) {
      cout << "impossible
";
      continue;
    }

    int nx = 0;

    for(int i = 0; i < n; i++)
      for(int j = -n+1; j <= n-1; j++)
        x[nx++] = h[i] + j*d;
    sort(x, x+nx);
    nx = unique(x, x+nx) - x;           ////把所有可能用到的修改结果都存放到x数组中

    int t = 0;
    for(int i = 0; i < nx; i++) {
      dp[0][i] = INF;
      if(x[i] == h[0]) dp[0][i] = 0;
    }
    for(int i = 1; i < n; i++) {
      int k = 0;                          ////因为只有奇,偶两种变化,且i只依赖于i-1时候的情况,因此可以用滚动数组优化
      for(int j = 0; j < nx; j++) {   //从小到大枚举x值,因为所有需要用到的x值都在x数组中,因此等价于枚举下标
                                       //dp[t][j]是这个状态(也就是改了i个数,改成了x[j]的表示)
        while(k < nx && x[k] < x[j]-d) //得到y, 前一个的高度
            k++;
        while(k+1 < nx && x[k+1] <= x[j]+d && dp[t][k+1] <= dp[t][k]) //这里其实是单调队列
            k++; // min in sliding window
        if(dp[t][k] == INF)
            dp[t^1][j] = INF;//之前的y都没有,肯定达不到目前的x
        else
            dp[t^1][j] = dp[t][k] + abs(x[j] - h[i]);  //得到x[j]处对应的最小值   j递增,abs就先减后增,(t,k)也先减后增
      }
      t ^= 1;
    }
    for(int i = 0; i < nx; i++)
      if(x[i] == h[n-1])
        cout << dp[t][i] << "
";
  }
  return 0;
}
原文地址:https://www.cnblogs.com/lqerio/p/9800700.html