uva12170 Easy Climb

Somewhere in the neighborhood we have a very nice mountain that gives
a splendid view over the surrounding area. There is one problem
though: climbing this moun- tain is very difcult, because of rather
large height differences. To make more people able to climb the
mountain and enjoy the view, we would like to make the climb eas- ier.
To do so, we will model the mountain as follows: the mountain consists
of n adjacent stacks of stones, and each of the stacks is h i high.
The successive height differences are therefore h i
+1

通过分析可以知道,每个位置最终的高度,一定是某个高度【也可以是自己】加上或减去若干个【最多n个】d【也可能不变】得到的值。从一个角度来看,这体现了一种极限,如果最后高度不是这个,增大或减小这个高度直到“触碰到”这个界限,一定仍然合法。而增大或者减小一定会有一个让结果变优。换一个角度来看,考虑某一个高度不变的点【比如第一个和最后一个】,他旁边的点的高度要么是自己、要么和他相差d。这样层层归纳下去也能得出结论。
可以把所有可能的高度【总共有O(n^2)个】离散化,用dp[i][j]表示前i个点,最后一个高度为j的最小费用,这样状态表示是O(n^3)的。
朴素的状态转移方程是dp[i][j]=min{abs(j-h[i])+dp[i-1][k]} (j-d<=k<=j+d)。O(n)转移,复杂度无法承受。
对于给定的i,j,abs(j-h[i])是定值,需要最小化的就是dp[i-1][k],这一项与j无关。于是对于每个i,从小到大枚举j的时候,用单调队列维护关于k的最优决策,做到O(1)转移。

#include
#include
#include
using namespace std;
#define LL long long
int n,m,que[50010];
const LL oo=0x3f3f3f3f3f3f3f3f;
LL h[110],a[50010],dp[110][50010],d;
void init()
{
    int i,j;
    scanf("%d%lld",&n,&d);
    m=0;
    for (i=1;i<=n;i++)
    {
        scanf("%lld",&h[i]);
        for (j=-n;j<=n;j++)
          if (h[i]+j*d>=0)
            a[++m]=h[i]+j*d;
    }
    sort(a+1,a+m+1);
    m=unique(a+1,a+m+1)-a-1;
    for (i=1;i<=n;i++)
      h[i]=lower_bound(a+1,a+m+1,h[i])-a;
}
void solve()
{
    int i,j,l,r,hd,tl;
    LL ans;
    memset(dp,0x3f,sizeof(dp));
    dp[1][h[1]]=0;
    for (i=2;i0;
        hd=1;
        tl=0;
        for (j=1;j<=m;j++)
        {
            while (r<=m&&a[r+1]<=a[j]+d)
            {
                r++;
                while (hd<=tl&&dp[i-1][que[tl]]>=dp[i-1][r]) tl--;
                que[++tl]=r;
            }
            while (hd<=tl&&a[que[hd]]if (hd<=tl) dp[i][j]=abs(a[h[i]]-a[j])+dp[i-1][que[hd]];
        }
    }
    ans=oo;
    for (j=1;j<=m;j++)
      if (abs(a[h[n]]-a[j])<=d)
        ans=min(ans,dp[n-1][j]);
    if (ans==oo) printf("impossible\n");
    else printf("%lld\n",ans);
}
int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        init();
        solve();
    }
}

你可能感兴趣的:(动态规划,数据结构,其他算法,UVa)