[HNOI2009]通往城堡之路
[HNOI2009]通往城堡之路
Time Limit: 10 Sec Memory Limit: 64 MB[Submit][Status][Discuss]
Description
听说公主被关押在城堡里,彭大侠下定决心:不管一路上有多少坎坷,不管城堡中的看守有多少厉害,不管救了公主之后公主会不会再被抓走,不管公主是否漂亮、是否会钟情于自己,他将义无反顾地朝着城堡前进。
可是,通往城堡的路上出现了一些情况。抽象地说,假象地图在二维平面的第一象限。在每个横轴的x位置上有一个高为hx的支撑点,如果彭大侠没有跳到支撑点上,那么他就会掉下去,牺牲在路途。开始时彭大侠在起点(1,h1)处,而城堡的入口在(n,hn)处。彭大侠每次可以从支撑点(x,hx)跳到支撑点(x+1,hx+1)。但是彭大侠每次的跳跃能量只有d,也就是说,每次跳跃必须满足条件|hx+1-hn|≤d。换句话说,如果两个相邻支撑点的纵向落差大于d,那么彭大侠就无法跳跃了!幸运的是,彭大侠还有一个杀手锏。在起点处,他可以花一个金币,把某个支撑点升高1个单位,或者降低1个单位。但是,起点处和城堡入口处的支撑点高度不能改变,并且一旦离开起点彭大侠就无法使用该杀手锏。
彭大侠被告知100个金币可兑换一单位生命。于是他希望通过少花金币来保存更多单位的生命。他终于找到了你这位热心的高手,请你帮他规划一下以便耗费尽量少的金币来到达城堡。
Input
文件第一行包含一个整数m(m≤5),表示问题求解次数。接下来的2m行依次表示每次求解的输入数据块。每个输入数据块占2行,其中第一行包含两个整数n和d,分别表示从起点到城堡入口处必须经过的支撑点数和每次跳跃允许的最大纵向落差,n和d之间用空格隔开,输入数据保证2≤n≤5000,0≤d≤109;第二行包含用空格隔开的n个非负整数h1、h2、…、hn,其中hi(1≤i≤n)表示第i个支撑点的高度,特别地,h1表示彭大侠出发时所在支撑点的高度,hn表示城堡入口所在支撑点的高度,输入数据保证对所有1≤i≤n有0≤hi≤109。
Output
有m行,第I(1≤I≤m)行表示第I次求解时彭大侠到达城堡必须耗费的最少金币数量。若无论怎样使用杀手锏他都无法到达城堡,则输出impossible。输入数据保证答案在int64范围之内。
Sample Input
10 2
4 5 10 6 6 9 4 7 9 8
3 1
6 4 0
4 2
3 0 6 3
Sample Output
impossible
4
HINT
对样例中的第一个输入数据块,d=2,把第三个支撑点降低3个单位,把第六个支撑点降低1个单位,把第七个支撑点升高2个单位,原序列变成:4 5 7 6 6 8 6 7 9 8,这时任意相邻支撑点的纵向落差没有超过2,彭大侠可以到达城堡!
对样例中的第二个输入数据块,d=1,这时不管怎样调节第二个支撑点的高度,都无法使任意相邻支撑点的纵向落差不超过1。
对样例中的第三个输入数据块,d=2,这时,把第二个支撑点升高1个单位,把第三个支撑点降低3个单位就满足条件了。
【数据规模】
20% n≤100
40% n≤1000
100% n≤5000
更为详细的题解即正确性分析:http://blog.csdn.net/ts124124/article/details/6249475
h为原始高度,b为最优解
先假设每个点都处于下限,即b[i]=h[1]-d*(i-1)
即每个点都处于不能再下降的状态
但此时最后一个点的高度不合题意,
所以通过调整b数组,使b[n]=a[n]
此时的b数组即为答案
每次调整,只让他们上升
而且每次上升区间[k,n],这样可以保证区间内部高度差<=d的限制
如何选k?
但实际上每个点的高度可能是下降的,即h[i]>b[i]
这种情况下,我们让i上升,实际上是减少花费
所以记录当前(原始高度>现在高度的个数)-(原始高度<现在高度的个数)sum
取sum最大的i为k,这段区间上升min(h[i]-b[i])
注意还要满足区间左端点与它前面的点高度差d的限制
#include<cstdio> #include<iostream> #include<algorithm> #include<cmath> #define N 5001 using namespace std; typedef long long LL; int n,d,h[N]; LL b[N]; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } int main() { int T,sum,maxs,k; LL ans,delta,mind; read(T); while(T--) { read(n); read(d); for(int i=1;i<=n;i++) read(h[i]); if(abs(h[1]-h[n])>1LL*(n-1)*d) { puts("impossible"); continue; } b[1]=h[1]; for(int i=2;i<=n;i++) b[i]=b[i-1]-d; while(h[n]!=b[n]) { sum=0,delta=2e9; maxs=-2e9,mind=2e9; for(int i=n;i>1;i--) { if(h[i]>b[i]) sum++,delta=min(delta,h[i]-b[i]); else sum--; if(maxs<sum && b[i-1]+d>b[i]) maxs=sum,k=i,mind=delta; } mind=min(mind,b[k-1]+d-b[k]); for(int i=k;i<=n;i++) b[i]+=mind; } ans=0; for(int i=2;i<=n;i++) ans+=abs(h[i]-b[i]); printf("%lld\n",ans); } return 0; }