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;
}