UVa12170

这道题我想出了一部分,还有另一部分没有想出,说明还需努力。

先说一说我想到的部分吧,首先用d(i,j)表示修改了前i个数,最后一个数为j(高度)所能够得到的最少花费,这个不难想到,

但是由于n<=10^9所以对时间空间来讲是行不通的,于是需要修改状态的定义,我想,这道题的高度j一定是一个特殊值,如果j取

任意都行的话那就不能够省去空间里,所以可能高度为前一个的顶部、不变(如果在范围内)、底部,然后就想不出来什么了。

我的思路是正确的,但差就差在推广与延伸,我们可以通过举3个数的例子不难看出一定是顶部、中间、底部(其实三个数可以证明,

多了我不会证,但在有时比赛上这种感觉往往是对的),我当时仅仅知道高度是前一个的顶部、不变、底部,并没有想到对于整体来说

高度的取值的规律,通过书的解释,我也明白了每个数修改之后一定可以写成hp+kd的形式,这样我们的状态数量变成了O(n^3)(可以看别的博客上面的说明),后面还有单调队列(与滑动窗口或使用数据结构)这个非常的巧妙,书上说平摊时间复杂度为O(1)(我

不知道平摊时间复杂度是什么?),然后感觉实现起来不好写(滑动窗口),看来刘汝佳代码发现十分巧妙(学到了),下面就是

代码:

// UVa 12170 
#include <cstdio>
#include <cstring> 
#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]; 
LL d; 

int main() { 
  int T, n; 
  scanf("%d", &T); 
  while (T--) {
      scanf("%d%lld", &n, &d); 
      for (int i = 0; i < n; ++i) scanf("%lld", &h[i]); 
      
      if (abs(h[0]-h[n-1]) > (n-1)*d) {
          printf("impossible\n"); 
          continue;
    }
      
      int x_size = 0; 
      for (int i = 0; i < n; ++i) 
        for (int k = -n+1; k <= n-1; ++k) 
          x[x_size++] = h[i] + k*d; 
      sort(x, x+x_size); 
      int nx = unique(x, x+x_size) - x; 
      
      int t = 0; 
      for (int i = 0; i < nx; ++i) {
            dp[t][i] = INF; 
            if (x[i] == h[0]) dp[t][i] = 0; 
      }
      for (int i = 1; i < n; ++i) {
             int k = 0;
             for (int j = 0; j < nx; ++j) {
                while (k < nx && x[k] < x[j]-d) k++;
                while (k+1 < nx && x[k+1] <= x[j]+d && dp[t][k+1] <= dp[t][k]) k++; 
                if (dp[t][k] == INF) dp[t^1][j] = INF; 
                else dp[t^1][j] = dp[t][k] + abs(h[i]-x[j]);   
            }
            t ^= 1; 
    }
    for (int i = 0; i < nx; ++i) 
        if (x[i] == h[n-1]) printf("%lld\n", dp[t][i]);  
  }
  return 0;
}

 

posted @ 2019-08-06 11:48  yifei_Wa  阅读(295)  评论(0编辑  收藏  举报