題目大意:有 n 個神奇寶貝,有 個普通球和 個超級球,對沒只神奇寶貝,使用普通球有 的概率抓獲,使用超級球有 的概率抓獲,普通球和超級球對每個神奇寶貝只能用一次,可以即使用普通球又使用超級球,求抓獲的神奇寶貝數量的最大期望值。
費用流做法:
對第 i 只神奇寶貝使用一個普通球對答案的貢獻爲 ,使用超級球的貢獻爲 ,都使用的貢獻爲 。
爲兩種球建立兩個源點 s1,s2
, s1 對每個神奇寶貝連一個容量爲 1,費用爲 的邊, s2 對每個神奇寶貝連一個容量爲 1,費用爲 的邊,如果對一個神奇寶貝用了兩種球,貢獻還要扣掉 ,因此不能直接對第 只神奇寶貝建 一條到 t,容量爲 2,費用爲0的邊。
考慮減掉這一部分的貢獻,每個神奇寶貝,建兩條到 的邊,容量均爲 1,其中一條費用爲0,另一條費用爲 ,如果對某個神奇寶貝只用了一種球,走的必然是 費用爲 0的邊,用了兩種球的話一定會走費用 的邊。
wqs二分做法:
設 dp[i][j][k]表示前 i 只神奇寶貝,使用 a 個普通球,b 個超級球的最大捕捉期望
,容易得到 的樸素 做法,對每隻神奇寶貝考慮使用球的組合進行轉移。
假如超級球的使用沒有限制,最優答案肯定是每隻神奇寶貝都使用超級球,設使用的超級球的個數爲,對應情況的最優答案爲 ,通過打表 容易發現 的增長幅度越來越小,也就是 是一個上凸的函數,可以二分一個代價 ,每用一個超級球,貢獻扣掉代價 ,顯然 的取值爲 [0,1], 越大,最優解使用的超級球越少,否則使用的超級球越多。
既然超級球可以二分,那麼普通球是不是也能同時二分,答案是顯然的,如果超級球剛好使用 個,二分代價 表示使用普通球的代價,單調性容易證明。在二分 後,二分超級球的使用 ,證明在普通球使用代價爲 的情況下,超級球的使用代價越高,最優解使用超級球的數量越少,即具有單調性。
設某次得到的最優解使用的普通球和超級球的數量分別爲 , 增長使用超級球的代價 ,反證:設 此時使用超級球的數量反而增多,普通球使用的數量爲 ,超級球使用的數量爲 ,滿足 。
無論 的關係如何,在增長超級球的代價 前,令 ,答案都會更優,即前面一次得到的不是最優解,與假設矛盾。因此可以同時二分普通球和超級球的使用代價,嵌套二分兩個代價同時與最優解的使用數量具有單調性。
兩層二分分別二分兩種球的使用代價,轉移時枚舉對第 i 只神奇寶貝使用哪種組合進行轉移:使用普通球,使用超級球,兩者都用。
複雜度爲 ,但代碼有點難寫,一般來說可能二分不到一個解使得它的使用數量恰好 = ,因此要讓每個解的使用數量儘可能多,但這題是雙關鍵字。參考了網上直接 ,沒有讓每個解的使用次數儘可能多,稍微改動一點就會 wa。
代碼:
#include<iostream>
#include<string.h>
#include<queue>
using namespace std;
const int maxn = 1e5 + 10;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
struct ss{
int v,c,nxt;
double w;
}edg[maxn];
int head[maxn],cnt,vis[maxn],pre[maxn];
double d[maxn];
void init() {
cnt = 0;
memset(head,-1,sizeof head);
}
void add(int u,int v,int c,double w) {
edg[cnt].v = v;
edg[cnt].c = c;
edg[cnt].w = w;
edg[cnt].nxt = head[u];
head[u] = cnt++;
edg[cnt].v = u;
edg[cnt].c = 0;
edg[cnt].w = -w;
edg[cnt].nxt = head[v];
head[v] = cnt++;
}
bool spfa(int s,int t) {
queue<int> q;
memset(vis,0,sizeof vis);
memset(pre,-1,sizeof pre);
for (int i = 0; i <= t; i++)
d[i] = -1e12;
q.push(s);
d[s] = 0; vis[s] = 1;
while(!q.empty()) {
int top = q.front();
q.pop();
vis[top] = 0;
for(int i = head[top]; i + 1; i = edg[i].nxt) {
int v = edg[i].v, c = edg[i].c; double w = edg[i].w;
if(c && d[v] < d[top] + w - eps) {
d[v] = d[top] + w; pre[v] = i;
if(!vis[v]) {
q.push(v);
vis[v] = 1;
}
}
}
}
return pre[t] != -1;
}
int maxflow(int s,int t,double &cost) {
int ans = 0;
cost = 0;
while(spfa(s,t)) {
int mx = inf;
for(int i = pre[t]; i != -1; i = pre[edg[i ^ 1].v]) {
mx = min(mx,edg[i].c);
}
for(int i = pre[t]; i != -1; i = pre[edg[i ^ 1].v]) {
edg[i].c -= mx;
edg[i ^ 1].c += mx;
cost += mx * edg[i].w;
}
ans += mx;
}
return ans;
}
int n,a,b;
double p[maxn],q[maxn];
int main() {
init();
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",&q[i]);
}
int s = 0, s1 = 2 * n + 1, s2 = 2 * n + 2, t = 2 * n + 3;
add(s,s1,a,0); add(s,s2,b,0);
for (int i = 1; i <= n; i++) {
add(s1,i,1,p[i]);
add(s2,i,1,q[i]);
add(i,t,1,0);
add(i,t,1,-p[i] * q[i]);
}
double cost; int f;
f = maxflow(s,t,cost);
printf("%lf\n",cost);
return 0;
}
wqs二分套wqs二分 代碼:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e3 + 10;
const double eps = 1e-8;
int n,a,b,ta[maxn],tb[maxn];
double p[maxn],q[maxn],dp[maxn],ans;
int calc(double ca,double cb,bool f) { //第二層二分,二分使用超級球的費用
for (int i = 1; i <= n; i++) {
dp[i] = dp[i - 1]; ta[i] = ta[i - 1]; tb[i] = tb[i - 1];
if (dp[i - 1] + p[i] - ca - dp[i] > eps) {
dp[i] = dp[i - 1] + p[i] - ca;
ta[i] = ta[i - 1] + 1;
tb[i] = tb[i - 1];
}
if (dp[i - 1] + q[i] - cb - dp[i] > eps) {
dp[i] = dp[i - 1] + q[i] - cb;
ta[i] = ta[i - 1];
tb[i] = tb[i - 1] + 1;
}
if (dp[i - 1] + q[i] + p[i] - q[i] * p[i] - ca - cb - dp[i] > eps) {
dp[i] = dp[i - 1] + q[i] + p[i] - q[i] * p[i] - ca - cb;
ta[i] = ta[i - 1] + 1;
tb[i] = tb[i - 1] + 1;
}
}
return f ? ta[n] : tb[n];
}
int main() {
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",&q[i]);
double l1 = 0, r1 = 1.0 ,l2 = 0, r2 = 0;
while (r1 - l1 > eps) {
double c1 = (l1 + r1) / 2;
l2 = 0, r2 = 1.0;
while (r2 - l2 > eps) {
double c2 = (l2 + r2) / 2;
if (calc(c1,c2,0) <= b) r2 = c2;
else l2 = c2;
}
if (calc(c1,r2,1) <= a) r1 = c1;
else l1 = c1;
}
calc(r1,r2,0);
printf("%lf\n",dp[n] + r1 * a + r2 * b);
return 0;
}