题目大意:给你n个数字,让你通过 +1、-1 将这些点的数值改变,使他们相邻的点之间的差 <= d ,问你最小的操作数。
思路:好题啊!如果不考虑数字范围,那么dp方程是很明显的,d[ i ][ j ] = min(d[i - 1][ k ]) ,j - h <= k < j+h,d[ i ][ j ] 表示前 i 个最后第i个数字为 j 的最小操作数。然后它 高度 和 d 的范围是 10^9,这里状态的精简才是这道题目的难点。但其实,这道题目的状态数最多只有 n*n*2 ,具体分析过程可以看这里:http://hi.baidu.com/sunhaowenprime/item/f7a379ba187467f663388e2d,真是又学习了。。。= = ,在深入一下,还可精简点状态,那就是求出h[ i ] 中的最大值、最小值,他们的状态肯定在这两者之间,然后就是用优先队列处理一下,不优化会TLE,时间复杂度O(n^3)。
自己想的时候,dp方程是很简单,但是一看数据范围就懵了,就是想不出怎么离散化,怎么精简状态,也有想到过那篇博客里说的三个位置,但是没有再深入的想下去,感觉凭这个不能优化。。。 = = ,还有话说这个优先队列,先开始自己还不会写,因为它只跟 i - 1有关系,每次更新一个j,队列的元素又不能变动,看了他的,发现他那个队列的插入删除真是妙啊。。 = =
代码如下:
#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long lld;
const lld INF = 0x0fffffffffffffff;
const int MAXN = 111;
int n;
lld d;
lld st[MAXN*MAXN<<1];
lld h[MAXN];
lld my_abs(lld a)
{
if(a<0) return -a;
else return a;
}
lld dp[MAXN][MAXN*MAXN<<1];
int front,rear;
struct Node
{
int s;
lld ret;
}q[MAXN*MAXN<<1];
void add(int s,lld ret)
{
while(front <= rear && q[rear].ret >= ret) rear--;
q[++rear].ret = ret;
q[rear].s = s;
}
lld get(int s)
{
while(front <= rear && my_abs(st[q[front].s]-st[s]) > d) front++;
if(front > rear) return INF;
return q[front].ret;
}
int main()
{
int _;
scanf("%d",&_);
while(_--)
{
scanf("%d%lld",&n,&d);
int tot = 0;
lld lm = INF,rm = 0;
for(int i = 1;i<=n;i++)
{
scanf("%lld",&h[i]);
lm = min(lm,h[i]);
rm = max(rm,h[i]);
}
for(int i = 1;i<=n;i++)
{
for(int j = -n;j<=n;j++)
{
lld tmp = h[i] + d*j;
if(tmp >= lm && tmp <= rm)
st[++tot] = h[i]+d*j;
}
}
sort(st+1,st+1+tot);
int m = unique(st+1,st+1+tot)-st-1;
for(int i = 1;i<=m;i++)
if(st[i] == h[1])
dp[1][i] = 0;
else dp[1][i] = INF;
//for(int i = 1;i<=m;i++)
//printf("i = %d,st[i] = %lld\n",i,st[i]);
for(int i = 2;i<=n;i++)
{
front = 1;
rear = 0;
int c = 1;
for(int j = 1;j<=m;j++)
{
while(c<=m && my_abs(st[j] - st[c]) <= d)
{
add(c,dp[i-1][c]);
c++;
}
dp[i][j] = get(j) + my_abs(st[j] - h[i]);
//printf("i = %d,j = %d,d = %lld,st[j] = %lld,get(j) = %lld\n",i,j,dp[i][j],st[j],get(j));
}
}
int x = lower_bound(st+1,st+1+m,h[n])-st;
lld ans = dp[n][x];
if(ans >= INF) puts("impossible");
else printf("%lld\n",ans);
}
return 0;
}