[Codeforces 193B Xor]暴搜

[Codeforces 193B Xor]暴搜

分类:Brute force

1. 题目链接

[Codeforces 193B Xor]

2. 题意描述

给定四个长度为 n,下标从 1 到 n 的数组 a, b, k, p,保证 p[1], p[2], …, p[n] 是 1, 2, …, n 的一个排列。你要对数组 a 进行恰好 u 次操作,每次可以在以下两种操作中选择一种:

  1. 对所有 i = 1, 2, …, n,将 a[i] 修改为 a[i] xor b[i];
  2. 对所有 i = 1, 2, …, n,将 a[i] 修改为 a[p[i]] + r。

请问,u 次操作之后, S = i = 1 n a i k i 的值最大可能是多少?1 <= n, u <= 30。

3. 解题思路

闲来逛知乎,看到这么一个《刷题时遇到过哪些巧妙的搜索题?》的一个回答。故决定尝试求解一番。

暴搜是 O(2^u) 的,会 TLE。如何剪枝?看起来这个最优化的目标没什么规律?不过,注意到操作 1 连做两次和没做一样,所以问题可以转成:操作次数和 u 奇偶性相同,操作 1 不能连做两次,最大化 s。这样的复杂度满足递归式 T(u) = T(u-1) + T(u-2),为 Fibonacci 数级别增长的复杂度,可以通过 u = 30 的数据。——引自知乎答主rsa

操作一的异或具有一种特殊性。连续异或多次的结果只能异或次数的奇偶性有关。
然后,我考虑枚举第二种操作的次数,然后根据奇偶关系(而非第一种操作的次数)来枚举第一种操作。
思路很简单,下面来分析一下这样做的算法复杂度:

设第一种操作次数记为 n u m A ,第二种操作次数记为 n u m B ,有 n u m A + n u m B = u
然后用插板法。 n u m B 个元素提供了 n u m B + 1 个空位。
把在空位中放入奇数个操作一,看成往这个空位上放入一个元素。
然后有以下两种情况:

  • n u m A 1 n u m B + 1 时,表示 n u m A 1 个元素可以填满 n u m B + 1 个位置。那么,只需要暴力枚举这 n u m B + 1 个位置的奇偶性即可。复杂度是 O ( 2 n u m B + 1 )
  • 否则,也就是说, n u m B + 1 个位置不能全被填满,那么复杂度为 O ( C n u m B + 1 n u m A 1 + C n u m B + 1 n u m A 3 + C n u m B + 1 n u m A 5 + ) 。这都是一个可以接受的复杂度。

不过答主所说的斐波那契复杂度也很好理解~

4. 实现代码

#include <bits/stdc++.h>
using namespace std;

typedef pair<int, int> pii;
typedef pair<long long, long long> pll;

const int inf = 0x3f3f3f3f;
const long long infl = 0x3f3f3f3f3f3f3f3fLL;

template<typename T> inline void umax(T &a, T b) { a = max(a, b); }
template<typename T> inline void umin(T &a, T b) { a = min(a, b); }
void debug() { cout << endl; }
template<typename T, typename ...R> void debug (T f, R ...r) { cout << "[" << f << "]"; debug (r...); }

const int MAXN = 35;

int n, u, r, numA, numB;
long long a[MAXN], b[MAXN], k[MAXN], p[MAXN];
long long ans;

void dfs(int cntA, int dep) {
    if (cntA > numA) return;
    long long t[MAXN], z[MAXN];
    memcpy(t, a, sizeof(a));
    if (dep == numB) {
        if ((numA - cntA) & 1) {
            for (int i = 0; i < n; ++i) {
                a[i] = a[i] ^ b[i];
            }
        }
        long long val = 0;
        for (int i = 0; i < n; ++i) val += a[i] * k[i];
        umax(ans, val);
        if ((numA - cntA) & 1) {
            memcpy(a, t, sizeof(t));
        }
        return;
    }
    for (int i = 0; i < n; ++i) a[i] = t[p[i]] + r;
    dfs(cntA, dep + 1);
    memcpy(a, t, sizeof(t));

    for (int i = 0; i < n; ++i) z[i] = a[i] ^ b[i];
    for (int i = 0; i < n; ++i) a[i] = z[p[i]] + r;
    dfs(cntA + 1, dep + 1);
    memcpy(a, t, sizeof(t));
}

int main() {
#ifdef ___LOCAL_WONZY___
    freopen("input.txt", "r", stdin);
#endif // ___LOCAL_WONZY___
    cin >> n >> u >> r;
    for (int i = 0; i < n; ++i) cin >> a[i];
    for (int i = 0; i < n; ++i) cin >> b[i];
    for (int i = 0; i < n; ++i) cin >> k[i];
    for (int i = 0; i < n; ++i) {
        cin >> p[i];
        -- p[i];
    }
    ans = -infl;
    for (numB = 0; numB <= u; ++numB) {
        numA = u - numB;
        dfs(0, 0);
    }
    cout << ans << endl;
#ifdef ___LOCAL_WONZY___
    cout << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC * 1000 << "ms." << endl;
#endif // ___LOCAL_WONZY___
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ACMore_Xiong/article/details/80035181