Description
link
Solution
Sub1 可以直接对于每个每个人问一次,考虑 Sub2 怎么做。
首先有个显然的想法是二分出第一个阳性的人,次数大概是 NPlogN,分数不高。
注意到每个位置的状态是随机生成的,并且题目要求的次数非常优,所以可以考虑利用期望 dp 求出最小期望操作次数。
设 fi,j 表示目前剩下 i 个人没有判断,且确定了有 j 个人,满足这 j 个人至少有一个阳性的最小期望操作次数。
对于 j=1 的情况,把那个已经确定阳了的人去掉,转移即为 fi,1←fi−1,0。
对于 j>1 的情况,考虑枚举 j 个人中的某 k 个,如果这 k 个人有阳的,就转移到 fi,k,这一部分的概率为 1−(1−P)j(1−P)k−(1−P)j。否则把这 k 个人去掉,即转移到 fi−k,j−k。所以:
fi,j=kmin{1−(1−P)j(1−P)k−(1−P)j⋅fi,k+(1−1−(1−P)j(1−P)k−(1−P)j)⋅fi−k,j−k}
对于 j=0 的情况,枚举 i 个人中的某 k 个,如果这 k 个人没有阳的,就转移到 fi−k,j−k,这一部分的概率为 (1−P)k。否则把这 k 个人去掉,转移到 fi,k。所以:
fi,0=kmin{(1−P)kfi−k,j−k+(1−(1−P)k)fi,k}
经过测试,当 P=0.2 时,f1000,0=727.957,可以通过。
输出方案可以通过 dp 过程中的最优转移点来做。
时间复杂度:O(n3)。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
| #include <bits/stdc++.h>
const int kMaxN = 1e3 + 5;
int n; int trans[kMaxN][kMaxN]; double p, pw[kMaxN], f[kMaxN][kMaxN]; std::mt19937 rnd(std::random_device{}());
bool test_students(std::vector<bool> mask) { assert(mask.size() == (size_t)n);
std::string mask_str(n, ' '); for (int i = 0; i < n; i++) mask_str[i] = mask[i] ? '1' : '0';
printf("Q %s\n", mask_str.c_str()); fflush(stdout);
char answer; scanf(" %c", &answer); return answer == 'P'; }
bool ask(std::vector<int> vec) { std::vector<bool> mask(n); for (auto x : vec) mask[x] = 1; return test_students(mask); }
void del(std::vector<int> &v1, std::vector<int> &v2) { static bool vis[kMaxN] = {0}; for (auto x : v1) vis[x] = 1; for (auto x : v2) vis[x] = 0; std::vector<int> tmp; std::swap(v1, tmp); for (auto x : tmp) { if (vis[x]) v1.emplace_back(x); vis[x] = 0; } }
void solve(std::vector<int> v1, std::vector<int> v2, std::vector<bool> &answer) { static bool vis[kMaxN] = {0}; int a = (int)v1.size(), b = (int)v2.size(); if (!a) return; if (b == 1) { answer[v2[0]] = 1; del(v1, v2); solve(v1, {}, answer); } else if (!b) { int k = trans[a][b]; std::vector<int> vec; for (int i = 0; i < k; ++i) vec.emplace_back(v1[i]); if (!ask(vec)) { del(v1, vec); solve(v1, {}, answer); } else { solve(v1, vec, answer); } } else { int k = trans[a][b]; std::vector<int> vec; for (int i = 0; i < k; ++i) vec.emplace_back(v2[i]); if (!ask(vec)) { del(v1, vec), del(v2, vec); solve(v1, v2, answer); } else { solve(v1, vec, answer); } } }
std::vector<bool> find_positive(bool op) { if (!op) { std::vector<bool> answer(n); for (int i = 0; i < n; ++i) { std::vector<bool> vec(n); vec[i] = 1; answer[i] = test_students(vec); } return answer; } else { std::vector<int> vec; std::vector<bool> answer(n); for (int i = 0; i < n; ++i) vec.emplace_back(i); solve(vec, {}, answer); return answer; } }
void prework() { pw[0] = 1; for (int i = 1; i <= n; ++i) pw[i] = pw[i - 1] * (1 - p); for (int i = 1; i <= n; ++i) { f[i][0] = 1e18, f[i][1] = f[i - 1][0]; for (int j = 2; j <= i; ++j) { f[i][j] = 1e18; for (int k = 1; k <= j; ++k) { double pr = (pw[k] - pw[j]) / (1 - pw[j]); double val = pr * f[i - k][j - k] + (1 - pr) * f[i][k] + 1; if (val < f[i][j]) { f[i][j] = val, trans[i][j] = k; } } } for (int j = 1; j <= i; ++j) { double pr = pw[j]; double val = pr * f[i - j][0] + (1 - pr) * f[i][j] + 1; if (val < f[i][0]) { f[i][0] = val, trans[i][0] = j; } } } }
int32_t main() { int T; scanf("%d %lf %d", &n, &p, &T); if (T > 1) prework(); for (int i = 0; i < T; i++) { std::vector<bool> answer = find_positive(T > 1); assert(answer.size() == (size_t)n);
std::string answer_str(n, ' '); for (int j = 0; j < n; j++) answer_str[j] = answer[j] ? '1' : '0';
printf("A %s\n", answer_str.c_str()); fflush(stdout);
char verdict; scanf(" %c", &verdict); if (verdict == 'W') exit(0); }
return 0; }
|