AT_abc326_f Robot Rotation 题解

发布时间 2023-10-31 10:18:35作者: RainPPR

AT_abc326_f Robot Rotation 题解

经典问题,以前遇到过一个类似的问题:[ABC082D] FT Robot

建议对比着看一看这两道题,是两种不同的思路。

(那一道题不用输出方案,因此可以用 bitset 优化;而此题需要输出方案,因此需要双向搜索。

思路

注意到每次只能「左转」和「左转」。

所以,偶数次走的只改变 \(x\) 坐标,奇数次走的只改变 \(y\) 坐标。

因此,我们可以将 \(x\) 方向和 \(y\) 方向的分开来看,然后在将这两部分合并。

然后考虑每个方向的是怎么算?

问题转化为:

给定序列 \(A\),可以改变任意元素的符号(正负),求一个方案,使得 \(\sum A=x\)

显然,这是一个背包问题啦。

但是这样算来,复杂度是 \(n\times2^n\) 的,明显不可过。

再仔细看看问题,我们发现可以双向搜索!

我们将序列分为左、右两半部分,对于每部分,分别求解,即求出每部分在该坐标轴上可以做出的贡献的集合,然后看看两个集合中是否有和为 \(x\) 的。

但是本题似乎要输出方案?

发现方案最多有 \(25\) 位,于是使用状压。

对于二进制数 \(k\),我们规定,从后往前第 \(i\) 为表示 \(A_i\) 是否变成负数。

合并方案?我们进行操作的时候,是先操作左面的(前面的)。

因此我们将两个二进制数拼接在一起就可以了。

输出方案的时候,可以记录一个当前的方向,就很容易得出每次的转向了:

  • 如果当前朝向«右»,接下来要往«上»走,则左转;
  • 如果当前朝向«右»,接下来要往«下»走,则右转;
  • (这个还用讲吗?。

注意:实现的时候,一定要注意二进制移位的细节!

时间复杂度:\(O(n\times2^{n/2})\)

代码

评测记录:https://atcoder.jp/contests/abc326/submissions/47085159

#include <bits/stdc++.h>

using namespace std;

using ll = long long;
using vi = vector<int>;
using ml = map<int, ll>;

#define endl '\n'
#define rep(i, n) for (int i = 0; i < (n); ++i)

#define rr read()
inline int read() {
    int num = 0, flag = 1, ch = getchar();
    for (; !isdigit(ch); ch = getchar()) if (ch == '-') flag = -1;
    for (; isdigit(ch); ch = getchar()) num = num * 10 + ch - '0';
    return num * flag;
}

ml mean(vi x) {
    ml ret[2]; ret[0][0] = 0ll;
    int k = 0; rep(i, x.size()) {
        ret[k ^= 1].clear(); for (auto t : ret[k ^ 1])
        ret[k][t.first + x[i]] = t.second | (1 << i), ret[k][t.first - x[i]] = t.second;
    } return ret[k];
}

ll solve(vi a, int x) {
    int n = a.size();
    vi L, R; rep(i, n) (i < n / 2 ? L : R).push_back(a[i]);
    ml l = mean(L), r = mean(R);
    for (auto i : l)
        if (r.count(x - i.first)) return i.second | (r[x - i.first] << n / 2);
    printf("No\n"), exit(0);
}

signed main() {
    int N = rr, X = rr, Y = rr; vi x, y;
    rep(i, N) (i & 1 ? x : y).push_back(rr);
    ll a = solve(x, X), b = solve(y, Y);
    printf("Yes\n");
    int d = 1; rep(i, N) {
        if (i & 1) putchar(((a >> i / 2) & 1) == d ? 'R' : 'L'), d = ((a >> (i >> 1)) & 1);
        else putchar(((b >> i / 2) & 1) == d ? 'L' : 'R'), d = ((b >> (i >> 1)) & 1);
    } return 0;
}