UVA12170 轻松爬山 Easy Climb

UVA12170 轻松爬山 Easy Climb

给定一个序列和一个数 d ,需要改变序列中的元素, 使得相邻元素的差值小于等于 d
求改变的最小值


エラー発生:没初始化no指针。。


Solution

我们定义 \(dp[i][j]\) 为 处理完第 i 座山, 且第 \(i\) 座山处理后的高度为 \(j\) 的最小花费
首先出现一个问题:j的状态总数过大
发现处理后的状态总是以 nd 成倍的增长
于是离散化一下

然后如果每次都枚举每个高度的话,复杂度将达到 \(O(n^{4})\) 无法接受
我们拿单调队列优化一下,可以省掉一轮枚举, 复杂度降至 \(O(n^{3})\)
题解的单调队列用法很妙
注意到上一轮的答案其实是个先减再增的
于是我们只用拿一个指针维护头即可

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<climits>
#define LL long long
#define REP(i, x, y) for(LL i = (x);i <= (y);i++)
using namespace std;
LL RD(){
    LL out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
const LL maxn = 110, inf = 1e18;
LL na, num, d;
LL h[maxn];
LL ori[1000010], no;
LL dp[maxn][1000010];
int init(){
	no = 0;
	num = RD(), d = RD();
	REP(i, 1, num){
		h[i] = RD();//读入并把可能的高度离散化
		REP(j, -num, num){
			ori[++no] = h[i] + j * d;
			}
		}
	if(abs(h[num] - h[1]) > (num - 1) * d){//判断合法
		puts("impossible");
		return 0;
		}
	sort(ori + 1, ori + 1 + no);
	no = unique(ori + 1, ori + 1 + no) - ori - 1;
	REP(i, 1, num){
		REP(j, 1, no){
			dp[i][j] = inf;
			}
		}
	REP(i, 1, no){
		if(ori[i] == h[1])dp[1][i] = 0;//边界
		}
	return 1;
	}
void work(){
	REP(i, 2, num){
		LL k = 1;
		REP(j, 1, no){
			while(k <= no && ori[k]  + d < ori[j])k++;//确认下界
				while(k + 1 <= no && ori[k + 1] <= ori[j] + d && dp[i - 1][k + 1] <= dp[i - 1][k])
					k++;//确认上界条件下 找到队头
			dp[i][j] = min(dp[i][j], dp[i - 1][k] + abs(ori[j] - h[i]));
			}
		}
	REP(i, 1, no){
		if(ori[i] == h[num])
			cout<<dp[num][i]<<endl;
		}
	}
int main(){
	na = RD();
	while(na--){
		if(!init())continue;
		work();
		}
	return 0;
	}
posted @ 2021-03-15 21:38  Tony_Double_Sky  阅读(103)  评论(0编辑  收藏  举报