D - 最少花费

Time Limit: 30000/10000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others)
 

Bob是一个魔法师。这天,他被N座山所阻挡,这些山排成一排,每座山有一个高度。Bob需要从左往右每次跳到相邻的一座山上,相邻的两座山的高度差不能超过K,当从高度差为D的一座山跳到另一座山时要花A×D的魔法值。Bob可以改变一座山的高度,但调整后的高度不能小于0或大于1000,改变S的高度需要花费S×S的魔法值。现在已知每座山的高度,求Bob跳完所有山后魔法值的最少花费。

Input

第一行一个整数T(T150),表示数据组数。

每组第一行有3个整数N(1N1000), K(0K1000), A(0A1000)。

接下来N个整数,按从左往右的顺序表示山的高度,山的高度在01000之间。

Output

对于每组数据,输出一个整数,表示最少花费。

Sample input and output

Sample InputSample Output
2
1 1 1
1
3 1 10
1 2 3
0
2

 

解题报告:

 我们令f( i , j ) 将第 i 座山高度改为 j 的最小花费.

  F( i , j ) = min ( f( i – 1 , k ) + abs ( j – k ) + (i – h[i]) ^ 2)

  不妨有 j > k

  F( i , j ) = min ( f( i – 1 , k ) – k ) + j + (i – h[i]) ^ 2

 即维护一个单调队列来更新 j > k的情况.

 J < k 的情况同理.

 

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
typedef long long ll;
using namespace std;
const int maxn = 1e3 + 50;
int a,n,dl,q[maxn];
ll  f[maxn][maxn],h[maxn];

int main(int argc,char *argv[])
{
  int Case;
  scanf("%d",&Case);
  while(Case--)
   {
         scanf("%d%d%d",&n,&dl,&a); //DL = deeplimit
         for(int i = 1 ; i <= n ; ++ i)
          scanf("%lld",&h[i]);
         for(int i = 1 ; i <= n ; ++ i)
          for(int j = 0 ; j <= 1000 ; ++ j)
           f[i][j] = 999999999999;
         for(int i = 0 ; i <= 1000 ; ++ i)
          f[1][i] = (i-h[1])*(i-h[1]);
         for(int i = 2 ; i <= n ; ++ i)
          {
                int front = 0 , rear = 0;
                for(int j = 0 ; j <= 1000 ; ++ j)
                 {
                       while(rear > front && f[i-1][q[rear-1]] - a*q[rear-1] >= f[i-1][j] - a*j)
                        rear--;
                       q[rear++] = j;
                       while(rear - front > 1 && j > q[front] + dl)
                        front++;
                       f[i][j] = min(f[i][j],(j-h[i])*(j-h[i])  +  f[i-1][q[front]] + (j - q[front]) * a);
           }
          front = rear = 0;
          for(int j = 1000 ; j >= 0 ; -- j)
           {
              while(rear > front && f[i-1][q[rear-1]] + q[rear-1]*a >= f[i-1][j] + j*a)
                  rear--;
                 q[rear++] = j;
                 while(rear - front > 1 && q[front] > j + dl)
                  front++;
                 f[i][j] = min(f[i][j],(j-h[i])*(j-h[i])  + f[i-1][q[front]] + (q[front] - j) * a );
           }
       }
      ll ans = 999999999999;
      for(int i = 0 ; i <= 1000 ; ++ i)
       ans = min(ans,f[n][i]);
      printf("%lld\n",ans);
   }
  return 0;
}

 

posted on 2015-05-29 23:27  天心散人  阅读(220)  评论(0编辑  收藏  举报