Codeforce 739 E.Gosha is hunting(费用流 | 期望dp + wqs二分套wqs二分优化)

在这里插入图片描述


题目大意:有 n 个神奇宝贝,有 a a a 个普通球和 b b b 个超级球,对没只神奇宝贝,使用普通球有 p [ i ] p[i] p[i] 的概率抓获,使用超级球有 q [ i ] q[i] q[i] 的概率抓获,普通球和超级球对每个神奇宝贝只能用一次,可以即使用普通球又使用超级球,求抓获的神奇宝贝数量的最大期望值。


费用流做法:
对第 i 只神奇宝贝使用一个普通球对答案的贡献为 p [ i ] p[i] p[i],使用超级球的贡献为 q [ i ] q[i] q[i],都使用的贡献为 p [ i ] + q [ i ] − p [ i ] ∗ q [ i ] p[i] + q[i] - p[i] * q[i] p[i]+q[i]p[i]q[i]

为两种球建立两个源点 s1,s2, s1 对每个神奇宝贝连一个容量为 1,费用为 p [ i ] p[i] p[i] 的边, s2 对每个神奇宝贝连一个容量为 1,费用为 q [ i ] q[i] q[i] 的边,如果对一个神奇宝贝用了两种球,贡献还要扣掉 p [ i ] ∗ q [ i ] p[i] * q[i] p[i]q[i],因此不能直接对第 i i i 只神奇宝贝建 一条到 t,容量为 2,费用为0的边。

考虑减掉这一部分的贡献,每个神奇宝贝,建两条到 t t t 的边,容量均为 1,其中一条费用为0,另一条费用为 − p [ i ] ∗ q [ i ] -p[i] * q[i] p[i]q[i],如果对某个神奇宝贝只用了一种球,走的必然是 费用为 0的边,用了两种球的话一定会走费用 − p [ i ] ∗ q [ i ] -p[i] * q[i] p[i]q[i] 的边。


wqs二分做法:
dp[i][j][k]表示前 i 只神奇宝贝,使用 a 个普通球,b 个超级球的最大捕捉期望,容易得到 n 3 n^3 n3 的朴素 d p dp dp 做法,对每只神奇宝贝考虑使用球的组合进行转移。

假如超级球的使用没有限制,最优答案肯定是每只神奇宝贝都使用超级球,设使用的超级球的个数为 x x x,对应情况的最优答案为 g ( x ) g(x) g(x)通过打表 容易发现 x , g ( x ) x,g(x) x,g(x) 的增长幅度越来越小,也就是 x , g ( x ) x,g(x) x,g(x) 是一个上凸的函数,可以二分一个代价 c c c,每用一个超级球,贡献扣掉代价 c c c,显然 c c c 的取值为 [0,1], c c c 越大,最优解使用的超级球越少,否则使用的超级球越多。

既然超级球可以二分,那么普通球是不是也能同时二分,答案是显然的,如果超级球刚好使用 b b b 个,二分代价 c 1 c1 c1 表示使用普通球的代价,单调性容易证明。在二分 c 1 c1 c1 后,二分超级球的使用 c 2 c2 c2,证明在普通球使用代价为 c 1 c1 c1 的情况下,超级球的使用代价越高,最优解使用超级球的数量越少,即具有单调性。

设某次得到的最优解使用的普通球和超级球的数量分别为 x 1 , y 1 x_1,y_1 x1,y1, 增长使用超级球的代价 c 2 c2 c2,反证:设 此时使用超级球的数量反而增多,普通球使用的数量为 x 2 x_2 x2,超级球使用的数量为 y 2 y_2 y2,满足 y 2 > y 1 y_2 > y_1 y2>y1

无论 x 1 和 x 2 x_1 和 x_2 x1x2 的关系如何,在增长超级球的代价 c 2 c2 c2 前,令 y 1 = y 2 y_1 = y_2 y1=y2,答案都会更优,即前面一次得到的不是最优解,与假设矛盾。因此可以同时二分普通球和超级球的使用代价,嵌套二分两个代价同时与最优解的使用数量具有单调性。

两层二分分别二分两种球的使用代价,转移时枚举对第 i 只神奇宝贝使用哪种组合进行转移:使用普通球,使用超级球,两者都用。

复杂度为 O ( n log ⁡ 2 n ) O(n \log^2n) O(nlog2n),但代码有点难写,一般来说可能二分不到一个解使得它的使用数量恰好 = k k k,因此要让每个解的使用数量尽可能多,但这题是双关键字。参考了网上直接 d p dp dp ,没有让每个解的使用次数尽可能多,稍微改动一点就会 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;
}

猜你喜欢

转载自blog.csdn.net/qq_41997978/article/details/104945030