题面传送
题意:给了\(n\)个堆的高度,要求改变堆的高度,首尾不可改变,使得队列的任意相邻的两数之差\(\leqslant d\),求最小代价。
令\(dp[i][j]\)表示将第\(i\)个堆的高度改为\(j\)时,\(1 \sim i\)的最小代价。
转移很好写:\(dp[i][j] = min\{dp[i - 1][k] \} + |a[i] - j| (j - d \leqslant k \leqslant j + d)\).
但是这样时空双爆。
用优先队列可以满足时间复杂度,但是空间复杂度怎么办呢?
这个就很妙了:\(h[i]\)虽然很大,但是\(n\)只有\(100\),所以肯定有很多状态用不上。
准确来说,只有\(a[i] + k * d(k\in [0,n])\)个数会用到。
根据一种“最优”的思想,在最小代价的前提下满足条件,一定是把原来差大于\(d\)的情况改成刚好等于\(d\)的情况,再改小了就是多余。
有了这个结论,dp数组的第二维最多只有\(n^2\)个了。这样我们离散化后,所有问题就解决了。
我也不知道为啥,这题不难,但是因为各种错误调了好长时间。
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<queue>
#include<assert.h>
#include<ctime>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
#define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
typedef long long ll;
typedef double db;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const db eps = 1e-8;
const int maxn = 102;
const int maxs = 5e4 + 2;
In ll read()
{
ll ans = 0;
char ch = getchar(), las = ' ';
while(!isdigit(ch)) las = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(las == '-') ans = -ans;
return ans;
}
In void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
int n, d;
int cnt = 0, _n;
ll a[maxn], b[maxs];
In void init()
{
cnt = 0;
for(int i = 1; i <= n; ++i)
for(int j = -n; j <= n; ++j)
if(a[i] + d * j >= 0) b[++cnt] = a[i] + d * j;
sort(b + 1, b + cnt + 1);
_n = unique(b + 1, b + cnt + 1) - b - 1;
}
ll dp[maxn][maxs];
struct Node{ll v; int p;};
deque<Node> q;
In ll DP()
{
Mem(dp, 0x3f);
dp[1][lower_bound(b + 1, b + _n + 1, a[1]) - b] = 0;
for(int i = 2; i < n; ++i)
{
q.clear();
for(int j = 1, x = 1; j <= _n; ++j)
{
while(x <= _n && b[x] <= b[j] + d)
{
while(!q.empty() && q.back().v >= dp[i - 1][x]) q.pop_back();
q.push_back((Node){dp[i - 1][x], x});
++x;
}
while(!q.empty() && b[q.front().p] < b[j] - d) q.pop_front();
if(!q.empty()) dp[i][j] = q.front().v + abs(a[i] - b[j]);
}
}
ll ret = INF;
for(int i = 1; i <= _n; ++i)
if(abs(b[i] - a[n]) <= d) ret = min(ret, dp[n - 1][i]);
return ret;
}
int main()
{
int T = read();
while(T--)
{
n = read(), d = read();
for(int i = 1; i <= n; ++i) a[i] = read();
init();
ll ans = DP();
if(ans == INF) puts("impossible");
else write(ans), enter;
}
return 0;
}