Description
link
Solution
Sub1 可以直接對於每個每個人問一次,考慮 Sub2 怎麼做。
首先有個顯然的想法是二分出第一個陽性的人,次數大概是 \(NP\log N\),分數不高。
注意到每個位置的狀態是隨機生成的,並且題目要求的次數非常優,所以可以考慮利用期望 dp 求出最小期望操作次數。
設 \(f_{i,j}\) 表示目前剩下 \(i\) 個人沒有判斷,且確定了有 \(j\) 個人,滿足這 \(j\) 個人至少有一個陽性的最小期望操作次數。
對於 \(j=1\) 的情況,把那個已經確定陽了的人去掉,轉移即為 \(f_{i,1}\leftarrow f_{i-1,0}\)。
對於 \(j>1\) 的情況,考慮列舉 \(j\) 個人中的某 \(k\) 個,如果這 \(k\) 個人有陽的,就轉移到 \(f_{i,k}\),這一部分的機率為 \(\frac{(1-P)^k-(1-P)^j}{1-(1-P)^j}\)。否則把這 \(k\) 個人去掉,即轉移到 \(f_{i-k,j-k}\)。所以:
\[f_{i,j}=\min_{k}{\left\{\frac{(1-P)^k-(1-P)^j}{1-(1-P)^j}\cdot f_{i,k}+\left(1-\frac{(1-P)^k-(1-P)^j}{1-(1-P)^j}\right)\cdot f_{i-k,j-k}\right\}}
\]
對於 \(j=0\) 的情況,列舉 \(i\) 個人中的某 \(k\) 個,如果這 \(k\) 個人沒有陽的,就轉移到 \(f_{i-k,j-k}\),這一部分的機率為 \((1-P)^k\)。否則把這 \(k\) 個人去掉,轉移到 \(f_{i,k}\)。所以:
\[f_{i,0}=\min_{k}{\left\{\left(1-P\right)^kf_{i-k,j-k}+\left(1-\left(1-P\right)^k\right)f_{i,k}\right\}}
\]
經過測試,當 \(P=0.2\) 時,\(f_{1000,0}=727.957\),可以透過。
輸出方案可以透過 dp 過程中的最優轉移點來做。
時間複雜度:\(O(n^3)\)。
Code
#include <bits/stdc++.h>
// #define int int64_t
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]); // 選的 k 個沒有的機率
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;
}