题目大意:一个机器人想要从(0,0) 位置走到 (x,y)位置,它有一个长为n操作序列,由UDLR四种字母组成,代表4种操作:
由于这个操作不一定能让它走到(x,y),但是你可以改动其中部分操作使得机器人最后还是在(x,y)这个位置。定义你改动的字母中,最左边和最右边的距离为你操作的距离,问要使得机器人最后在x,y 位置,你的修改操作的最小距离是多少。
题解:如果想要贪心构造解使得改动的距离最小,那基本凉了。由于距离是由你最左边的改动的位置和最右边改动的位置决定的,中间的你可以随便改,并没有限制你改的个数和方案。
这相当于你可以随意改动一个连续的区间的操作,而你只关心最后能否到达(x,y),并不关系在这个区间里你改成了什么样。
先观察什么情况下无解:最差情况你可以修改整个数组,让它径直走向(x,y),显然操作次数不够,或者次数太多上下移动抵消不了就无解,其它情况下一定有解。若无解直接输出-1,若有解:
假设你确定要修改[L,R]区间内的所有操作序列(随意改动成你要的样子),如何判断你能否到达(x,y):
在你改动范围之外的操作最终会走到一个点,用前缀和O(1)判断出你会落在哪个点,计算落点和(x,y)的距离,这样你只需要判断你修改的区间的长度是否够长支持你从落点走向(x,y)就行。
容易观察到答案具有单调性:若[L,R]是一个答案,那么[L + 1,R],[L,R + 1],所有包含[L,R]的区间都可以成为答案,因为你可以不修改多余的那部分,而在长度确定的情况下枚举区间的复杂度是O(n)的,于是就有一个O(nlogn)的二分做法。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
int n,x,y;
char s[maxn];
int sl[maxn],sr[maxn],su[maxn],sd[maxn];
int main() {
scanf("%d",&n);
scanf("%s",s + 1);
scanf("%d%d",&x,&y);
for(int i = 1; i <= n; i++) {
if(s[i] == 'L') sl[i]++;
if(s[i] == 'R') sr[i]++;
if(s[i] == 'U') su[i]++;
if(s[i] == 'D') sd[i]++;
}
for(int i = 1; i <= n; i++) {
sl[i] += sl[i - 1];
sr[i] += sr[i - 1];
su[i] += su[i - 1];
sd[i] += sd[i - 1];
}
int l = 0, r = n + 1;
while(l < r) {
int mid = l + r >> 1;
bool f = false;
for(int i = 1; i + mid - 1 <= n; i++) {
int ed = i + mid - 1;
int nx = sr[n] - sr[ed] + sr[i - 1];nx -= sl[n] - sl[ed] + sl[i - 1];
int ny = su[n] - su[ed] + su[i - 1];ny -= sd[n] - sd[ed] + sd[i - 1];
int d = abs(nx - x) + abs(ny - y);
if(mid >= d && (mid - d) % 2 == 0) f = true;
}
if(f) r = mid;
else l = mid + 1;
}
printf("%d\n",l == n + 1 ? -1 : l);
return 0;
}
更特殊的,由于询问是区间形式,而答案区间具有单调性,枚举区间可以用双指针(也就是尺取法),因为:若[L,R] 不是答案,那么[L + 1,R],[L + 2,R] …,[R - 1,R] 一定也不是答案,这些区间不必去枚举。若[L,R]是答案,则[L + 1,R],…,[L + X,R] 中可能也是答案,可以移动左指针直到这个区间不能成为答案为止。
观察到区间的枚举量减少了很多,整体复杂度是O(n)的,因为左右指针各移动 n 次。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
int n,x,y;
char s[maxn];
int sl[maxn],sr[maxn],su[maxn],sd[maxn];
int main() {
scanf("%d",&n);
scanf("%s",s + 1);
scanf("%d%d",&x,&y);
int ed = abs(x) + abs(y);
if(n < ed || (n - ed) % 2 != 0) {
puts("-1");
return 0;
}
for(int i = 1; i <= n; i++) {
if(s[i] == 'L') sl[i]++;
if(s[i] == 'R') sr[i]++;
if(s[i] == 'U') su[i]++;
if(s[i] == 'D') sd[i]++;
}
for(int i = 1; i <= n; i++) {
sl[i] += sl[i - 1];
sr[i] += sr[i - 1];
su[i] += su[i - 1];
sd[i] += sd[i - 1];
}
int l = 0, r = 1;
int ans = 0x3f3f3f3f;
if(sr[n] - sl[n] == x && su[n] - sd[n] == y) ans = 0;
for(r = 1; r <= n; r++) {
int nx = sr[n] - sr[r] + sr[l];nx -= sl[n] - sl[r] + sl[l];
int ny = su[n] - su[r] + su[l];ny -= sd[n] - sd[r] + sd[l];
int d = abs(nx - x) + abs(ny - y);
int len = r - l;
while(l < r && len >= d) {
ans = min(ans,len);
l++;
nx = sr[n] - sr[r] + sr[l];nx -= sl[n] - sl[r] + sl[l];
ny = su[n] - su[r] + su[l];ny -= sd[n] - sd[r] + sd[l];
d = abs(nx - x) + abs(ny - y);
len = r - l;
}
}
printf("%d\n",ans == 0x3f3f3f3f ? -1 : ans);
return 0;
}