题目链接:点击这里
题目大意:
给定一个长度为
n
n
n 的字符串,每一位代表一种向上下左右某个方向移动一步的操作,现在希望修改序列使之走到坐标
(
x
,
y
)
(x,y)
(x,y) ,修改花费为
m
a
x
I
D
−
m
i
n
I
D
maxID-minID
maxID−minID ,其中
m
a
x
I
D
maxID
maxID 表示修改的点中的最大下标,
m
i
n
I
D
minID
minID 表示修改的点中的最小下标,求使其走到
(
x
,
y
)
(x,y)
(x,y) 的最小花费
题目分析:
花费显然是具有单调性的,故考虑二分答案。接下来考虑如何写
c
h
e
c
k
(
v
a
l
)
check(val)
check(val) ,我们可以枚举所有长度为
v
a
l
val
val 的区间,看修改该区间是否可以符合条件。
此时问题就转换成了,如何判断一个区间被修改后能到达
(
x
,
y
)
(x,y)
(x,y) ,我们可以预处理出
p
r
e
x
[
i
]
,
p
r
e
y
[
i
]
prex[i],prey[i]
prex[i],prey[i] ,分别表示前
i
i
i 步操作后的横纵坐标。如果忽略当前枚举的区间的贡献后距离
(
x
,
y
)
(x,y)
(x,y) 的曼哈顿距离
d
i
s
dis
dis 是否小于等于
v
a
l
val
val ,而且
v
a
l
−
d
i
s
val-dis
val−dis 使偶数(多余步数让其反复横跳花费掉),那么当前区间就是合法的。
具体细节见代码:
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn = 2e5+5;
int n,x,y,prex[maxn],prey[maxn],ans = -1;
string s;
bool check(int val)
{
for(int l = 1,r = val;r <= n;l++,r++)
{
int dx = prex[n]-(prex[r]-prex[l-1]);
int dy = prey[n]-(prey[r]-prey[l-1]);
if(abs(dx-x)+abs(dy-y)<=val && (val-abs(dx-x)-abs(dy-y))%2==0) return true;
}
return false;
}
int main()
{
cin>>n>>s>>x>>y;
for(int i = 1;i <= n;i++)
{
prex[i] = prex[i-1];
prey[i] = prey[i-1];
if(s[i-1] == 'U') prey[i]++;
if(s[i-1] == 'D') prey[i]--;
if(s[i-1] == 'L') prex[i]--;
if(s[i-1] == 'R') prex[i]++;
}
int l = 0,r = n;
while(l <= r)
{
int mid = l+r>>1;
if(check(mid))
{
ans = mid;
r = mid-1;
}
else l = mid+1;
}
printf("%d\n",ans);
return 0;
}