QOJ 4832 Telepathy

乍一看好像怎么选都是 \(1 / 2\) 概率。

假设长度为 \(2\),或者可以理解为把串分为了长度为 \(2\) 的一个个小串。
考虑加上一点策略,对于 \(\text{Flim}\),为 \(\texttt{01}\) 就选 \(0 / 1\) 这样的。
感觉好像还是 \(1 / 2\),但是考虑以下例子:

\(\text{Flim}\) 此时是 \(\texttt{10}\),选 \(0\)
对于 \(\text{Flam}\)\(\texttt{00, 01}\)\(1\)\(\texttt{10, 11}\)\(0\)

那么能够发现对于 \(\text{Flam}\) 不管选什么都是能匹配上的。
所以发现对于 \(\text{Flim}\) 一个串概率并不一定是 \(1 / 2\),而是可能像上面这样甚至到达了 \(1\)
进一步的,考虑以下的选取方案:

对于 \(\text{Flim}\)\(\texttt{00, 01, 11}\)\(1\)\(\texttt{10}\)\(0\)
对于 \(\text{Flam}\)\(\texttt{00, 01}\)\(1\)\(\texttt{10, 11}\)\(0\)

手玩后能发现对于 \(4\times 4 = 16\) 种情况,有 \(10\) 种匹配成功。
也就是说匹配成功的概率达到了 \(10 / 16 = 62.5\%\)

以下是打表代码:

#include<bits/stdc++.h>
int c[8], p[8];
// f[] -> 状态对应的选 0 / 1
// 状态: x * 4 + y * 2 + z * 1
// x -> Flim 0; Flam 1
// y, z -> 长度为 2 的串的值
int main() {
    int mx = 0;
    for (int s = 0; s < (1 << 8); s++) {
        for (int i = 0; i < 8; i++) c[i] = s >> i & 1;
        int cnt = 0;
        for (int x = 0; x < 4; x++) for (int y = 0; y < 4; y++) {
            int xa[2] = {x >> 1 & 1, x & 1}, ya[2]= {y >> 1 & 1, y & 1};
            cnt += xa[c[4 + y]] == ya[c[x]];
        }
        if (cnt > mx) mx = cnt, memcpy(p, c, sizeof(p));
    }
    printf("mx = %d\n", mx);
    for (int i = 0; i < 8; i++) printf("%d", p[i]);
    return 0;
}

但是这还不够,考虑前面分的是长度为 \(2\) 的小串。
于是再加上一个字符,按长度为 \(3\) 来分,再来看一次结果。

能发现匹配成功的次数达到了 \(44\),概率为 \(44 / 64 = 68.75\%\),能够通过。
打表代码:

// 意义同上
#include<bits/stdc++.h>
int c[16], p[16];
int main() {
    int lim = 1;
    for (int i = 1; i <= 16; i++, lim *= 3);
    int mx = 0;
    for (int s = 0; s < lim; s++) {
        for (int i = 0, x = s; i < 16; i++) c[i] = x % 3, x /= 3;
        int cnt = 0;
        for (int x = 0; x < 8; x++) for (int y = 0; y < 8; y++) {
            int xa[3] = {x >> 2 & 1, x >> 1 & 1, x & 1}, ya[3]= {y >> 2 & 1, y >> 1 & 1, y & 1};
            cnt += xa[c[8 + y]] == ya[c[x]];
        }
        if (cnt > mx) mx = cnt, memcpy(p, c, sizeof(p));
    }
    printf("mx = %d\n", mx);
    for (int i = 0; i < 16; i++) printf("%d", p[i]);
    return 0;
}

据官方题解所说把小串的串长继续拉大概率能接近 \(70\%\),但这个打表程序已经跑不动了。

直接把打表程序得到的对应方案与输入相匹配输出即可。

#include<bits/stdc++.h>
const int maxn = 1e6 + 10, limn = 1e6, limk = 1e5;
int c[16] = {0, 1, 2, 2, 0, 1, 0, 0, 0, 1, 2, 2, 0, 1, 0, 0};
char op[5], s[maxn];
int a[maxn];
int main() {
    scanf("%s%*d%*d%s", op, s + 1);
    for (int i = 1; i <= limn; i++) a[i] = s[i] - '0';
    for (int i = 1; i <= limk * 3; i += 3) printf("%d ", i + c[(op[2] == 'a') * 8 + a[i] * 4 + a[i + 1] * 2 + a[i + 2]]);
    return 0;
}
posted @ 2024-01-21 19:25  lhzawa  阅读(46)  评论(0)    收藏  举报