UVA12170 Easy Climb

题面传送


题意:给了\(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;
}
上一篇:PKI系列:(5)证书下的Easy ***【远程访问 硬件与软件拨号】


下一篇:ABC101 题解