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<cstdio>
#include<cstring>
#include<algorithm>
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;i<n;i++)
    {
        r=0;
        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]]<a[j]-d) 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();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值