CodeForces 739E Gosha is hunting
題目大意
有個精靈,Gosha 手中有個普通球和個超級球,已知第個精靈被普通球捕獲的概率爲,被超級球捕獲的概率爲。Gosha 可以先決定對哪些精靈扔普通球,哪些扔超級球。
Gosha 想知道在最優決策下,捕獲的精靈的最大期望數量。
注意可以向一個精靈扔最多一個普通球和超級球。這時這個精靈被這些球中的任何一個球捕獲到都算作被捕獲,但其他的球仍然被消耗掉。
分析
考慮對一個精靈的四種扔球情況的期望:
- 不扔球:捕獲概率和期望都是;
- 扔一個普通球:捕獲概率爲,對答案貢獻;
- 扔一個超級球:捕獲概率爲,對答案貢獻爲;
- 扔一個超級球和一個普通球:捕獲概率爲,對答案貢獻爲。
我們很自然地想到兩種做法:
網絡流
我們用邊的容量去限制球的使用個數,用費用去計算貢獻。
球的總個數限制可以用邊的容量去限制:建立兩個節點,分別向源點連一條容量爲,費用爲的邊。
那麼對於第個精靈,我們從連一條到第個點,容量爲,費用爲的邊,從連一條到第個點,容量爲,費用爲的邊。
那麼精靈怎麼向匯點連邊?
考慮到若過來的流量只有,對於情況2,3,顯然貢獻爲或者,這是沒問題的。
但過來的流量是呢?我們需要減掉。
換句話說我們需要更大的流量才能夠獲得的貢獻。
一種有效的方法是將一條邊拆成兩條:一條費用爲,容量爲的邊和一條費用爲,容量爲的邊。
由於費用流是按照最短路來增廣的,所以一定會先去那條費用爲的邊,再去費用爲的邊。故這樣建圖沒有問題。
注意我們要求的是最大費用最大流,所以爲了將這個問題變成最小費用最大流,代碼中所有的邊權都取反了。
帶權二分
考慮用 DP 來解決這道題:
定義狀態爲對前個精靈用了個普通球和個精靈球的最大期望。
轉移就按照上面講的四種情況來就是了。最終答案就是了。
換句話說我們最大的答案一定是將用完了的。
似乎有點像帶權二分。
由於我們需要最大化答案,所以我們一定是先扔期望大的再扔期望小的,所以最優解的增長趨勢一定會越來越慢,所以這個函數就是個凸的。 (手動腦補三維空間上的函數圖像。。。)
於是我們就可以愉快地帶權二分套帶權二分了QAQ。。。
參考代碼
備註: 這題卡精度,不用 EPS 和 EPS 太小都過不去 QAQ。。。
經測試當做 EPS 是一個不錯的選擇。
寫二分時不想卡精度就直接用循環次數好了。。。
網絡流
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int Maxn = 2000;
const int Maxm = 1e4;
const int INF = 0x3f3f3f3f;
const double INFDB = (1 << 30);
const double EPS = 1e-7;
inline bool cmp(double lhs, double rhs) {
return lhs - rhs < -EPS;
}
inline bool equal(double lhs, double rhs) {
return -EPS < lhs - rhs && lhs - rhs < EPS;
}
struct MaxCostMaxFlow {
struct Edge {
int to, cap;
double cos;
Edge *nxt, *bak;
};
Edge pool[Maxm * 2 + 5];
Edge *ecnt, *G[Maxn + 5];
inline void addedge(int u, int v, int cap, double c) {
Edge *p = ++ecnt;
p->to = v, p->cap = cap, p->cos = c;
p->nxt = G[u], G[u] = p;
p->bak = p + 1;
p = ++ecnt;
p->to = u, p->cap = 0, p->cos = -c;
p->nxt = G[v], G[v] = p;
p->bak = p - 1;
}
int node, s, t;
double d[Maxn + 5];
Edge *cpy[Maxn + 5];
double cost;
void init(int n) {
ecnt = &pool[0];
node = n;
for(int i = 1; i <= n; i++)
G[i] = NULL;
}
bool vis[Maxn + 5];
int dfs(int u, int tot) {
if(u == t) return tot;
vis[u] = true;
int sum = 0;
for(;cpy[u] != NULL; cpy[u] = cpy[u]->nxt) {
int v = cpy[u]->to;
double w = cpy[u]->cos;
if(!vis[v] && equal(d[v], d[u] + w) && cpy[u]->cap > 0) {
int delta = dfs(v, min(tot - sum, cpy[u]->cap));
cpy[u]->cap -= delta, cpy[u]->bak->cap += delta;
cost += cpy[u]->cos * delta;
sum += delta;
if(sum == tot) break;
}
}
vis[u] = false;
return sum;
}
bool spfa(int _s, int _t) {
static bool inq[Maxn + 5];
memset(inq, false, sizeof inq);
for(int i = 1 ; i <= node; i++)
d[i] = INFDB, cpy[i] = G[i];
queue<int> q;
q.push(_s), d[_s] = 0, inq[_s] = true;
while(!q.empty()) {
int u = q.front();
q.pop(), inq[u] = false;
for(Edge *p = G[u]; p != NULL; p = p->nxt) {
int v = p->to;
if(p->cap > 0 && cmp(d[u] + p->cos, d[v])) {
d[v] = d[u] + p->cos;
if(!inq[v]) {
inq[v] = true;
q.push(v);
}
}
}
}
return d[_t] != INFDB;
}
double mcmf(int _s, int _t) {
s = _s, t = _t, cost = 0;
while(spfa(s, t)) {
memset(vis, false, sizeof vis);
dfs(s, INF);
}
return cost;
}
};
int N, A, B;
double P[Maxn + 5], U[Maxn + 5];
MaxCostMaxFlow f;
int main() {
#ifdef LOACL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
scanf("%d %d %d", &N, &A, &B);
for(int i = 1; i <= N; i++)
scanf("%lf", &P[i]);
for(int i = 1; i <= N; i++)
scanf("%lf", &U[i]);
int s = N + 1, t = N + 2, p = N + 3, u = N + 4;
f.init(N + 4);
f.addedge(s, p, A, 0), f.addedge(s, u, B, 0);
for(int i = 1; i <= N; i++) {
f.addedge(p, i, 1, -P[i]), f.addedge(u, i, 1, -U[i]);
f.addedge(i, t, 1, 0), f.addedge(i, t, 1, P[i] * U[i]);
}
printf("%lf\n", -f.mcmf(s, t));
return 0;
}
帶權二分
#include <cstdio>
#include <algorithm>
using namespace std;
const int Maxn = 2000;
int N, A, B;
double P[Maxn + 5], U[Maxn + 5];
double f[Maxn + 5];
int cntp[Maxn + 5], cntu[Maxn + 5];
bool check(double p, double u, int typ) {
for(int i = 1; i <= N; i++) {
f[i] = f[i - 1], cntp[i] = cntp[i - 1], cntu[i] = cntu[i - 1];
if(f[i - 1] + P[i] - p > f[i])
f[i] = f[i - 1] + P[i] - p, cntp[i] = cntp[i - 1] + 1,
cntu[i] = cntu[i - 1];
if(f[i - 1] + U[i] - u > f[i])
f[i] = f[i - 1] + U[i] - u, cntu[i] = cntu[i - 1] + 1,
cntp[i] = cntp[i - 1];
if(f[i - 1] + U[i] + P[i] - U[i] * P[i] - p - u > f[i])
f[i] = f[i - 1] + U[i] + P[i] - U[i] * P[i] - p - u,
cntu[i] = cntu[i - 1] + 1, cntp[i] = cntp[i - 1] + 1;
}
return typ == 0 ? cntu[N] > B : cntp[N] > A;
}
int main() {
#ifdef LOACL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
scanf("%d %d %d", &N, &A, &B);
for(int i = 1; i <= N; i++)
scanf("%lf", &P[i]);
for(int i = 1; i <= N; i++)
scanf("%lf", &U[i]);
double lb = 0, ub = 1, lb1, ub1;
for(int i = 1; i <= 50; i++) {
double mid = (lb + ub) / 2;
lb1 = 0, ub1 = 1;
for(int i = 1; i <= 50; i++) {
double mid1 = (lb1 + ub1) / 2;
if(check(mid, mid1, 0))
lb1 = mid1;
else ub1 = mid1;
}
if(check(mid, ub1, 1)) lb = mid;
else ub = mid;
}
check(ub, ub1, 0);
printf("%lf\n", f[N] + A * ub + B * ub1);
return 0;
}