CF739E Gosha is hunting 费用流 概率期望

版权声明:本文为博主原创文章,可以转载但是必须声明版权。 https://blog.csdn.net/forever_shi/article/details/89378010

题目链接

题意:
你要抓神奇宝贝! 现在一共有 n n 只神奇宝贝。 你有 a a 个宝贝球和 b b 个超级球。宝贝球抓到第 i i 只神奇宝贝的概率是 p i p_i ,超级球抓到的概率则是 u i u_i 。不能往同一只神奇宝贝上使用超过一个同种的球,但是可以往同一只上既使用宝贝球又使用超级球(都抓到算一个)。 请合理分配每个球抓谁,使得你抓到神奇宝贝的总个数期望最大,并输出这个值。 n < = 2000 n<=2000

题解:
据说是可以凸优化,看上去当场好多随机算法(可能是退火)+dp的过了的样子。我这个题用的是网络流的方法。

首先肯定是一个费用流,费用表示概率,流量表示用的球数。主要的问题在于,我们对同一个神奇宝贝,我们对他扔了两个球,但是只能抓到一次。我们要求最大期望,于是应该要用一个最大费用最大流。我们考虑如果只扔一个球,那么抓到的期望就是这个概率,那么两个都扔出去的话,我们流经的费用之和是第一个抓到,第二个随便的概率加上第二个抓到,第一个随便的概率。我们重复计算的部分是两个都抓到的部分。于是我们就可以开始建图了。建图的方法是源点向两种球连流量为每种球个数,费用为 0 0 的边。然后每种球向每个神奇宝贝连流量是 1 1 ,费用为概率的边,表示一种球只能对一种神奇宝贝扔一次,抓到的概率就是给出的概率。然后对于每个神奇宝贝,我们向汇点连两条边。第一条边是流量为 1 1 ,费用为 0 0 的边,表示第一次通过时增加的量就是用某个球抓到这个神奇宝贝的概率。第二条边是流量为 1 1 ,费用为两个球都抓到这个神奇宝贝的概率乘积的相反数。这样第二条边流量是非正的,于是最大费用最大流的时候一定不会出现先经过它而出错的情况(如果是0,经过两条是等价的,如果是负数,最大费用最大流不会只经过这条有负权的)。而这样第二次经过的时候,正好可以把重复计算的减掉,这样就可以得出正确答案了。

还有一个细节需要注意,在费用流的时候需要有一个eps来判断费用的大小关系,不然可能会在相等的时候因为精度误差出现死循环。

代码:

#include <bits/stdc++.h>
using namespace std;

int n,aa,bb,hed[40010],cnt,f[40010],inq[4010],st,ed;
double qwq1[40010],qwq2[40010],ans,v[4010],w[4010];
const double eps=1e-8;
queue<int> q;
struct node
{
	int from,to,next;
	double c,cost;
}a[200010];
inline void add(int from,int to,double c,double cost)
{
	a[++cnt].from=from;
	a[cnt].to=to;
	a[cnt].c=c;
	a[cnt].cost=cost;
	a[cnt].next=hed[from];
	hed[from]=cnt;
	a[++cnt].from=to;
	a[cnt].to=from;
	a[cnt].c=0;
	a[cnt].cost=-cost;
	a[cnt].next=hed[to];
	hed[to]=cnt;
}
inline void bfs()
{
	for(int i=1;i<=ed;++i)
	{
		w[i]=0;
		v[i]=-2e9;
	}
	w[st]=2e9;
	v[st]=0;
	q.push(st);
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		inq[x]=0;
		for(int i=hed[x];i;i=a[i].next)
		{
			int y=a[i].to;
			if(a[i].c>eps&&v[y]+eps<v[x]+a[i].cost)
			{
				v[y]=v[x]+a[i].cost;
				w[y]=min(a[i].c,w[x]);
				f[y]=i;
				if(!inq[y])
				{
					q.push(y);
					inq[y]=1;
				}
			}
		}
	}
	for(int i=f[ed];i;i=f[a[i].from])
	{
		a[i].c-=w[ed];
		a[i^1].c+=w[ed];
	}
}
int main()
{
	scanf("%d%d%d",&n,&aa,&bb);
	cnt=1;
	st=n+3;
	ed=n+4;
	add(st,n+1,aa,0);
	add(st,n+2,bb,0);
	for(int i=1;i<=n;++i)
	{
		double x;
		scanf("%lf",&x);
		qwq1[i]=x;
		add(n+1,i,1,x);
		add(i,ed,1,0);
	}
	for(int i=1;i<=n;++i)
	{
		double x;
		scanf("%lf",&x);
		qwq2[i]=x;
		add(n+2,i,1,x);
		add(i,ed,1,-qwq2[i]*qwq1[i]);
	}
	while(1)
	{
		bfs();
		if(w[ed]>eps)
		ans+=w[ed]*v[ed];
		else
		break;
	}
	printf("%.10lf\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/89378010