题意:
你要抓神奇宝贝! 现在一共有
只神奇宝贝。 你有
个宝贝球和
个超级球。宝贝球抓到第
只神奇宝贝的概率是
,超级球抓到的概率则是
。不能往同一只神奇宝贝上使用超过一个同种的球,但是可以往同一只上既使用宝贝球又使用超级球(都抓到算一个)。 请合理分配每个球抓谁,使得你抓到神奇宝贝的总个数期望最大,并输出这个值。
题解:
据说是可以凸优化,看上去当场好多随机算法(可能是退火)+dp的过了的样子。我这个题用的是网络流的方法。
首先肯定是一个费用流,费用表示概率,流量表示用的球数。主要的问题在于,我们对同一个神奇宝贝,我们对他扔了两个球,但是只能抓到一次。我们要求最大期望,于是应该要用一个最大费用最大流。我们考虑如果只扔一个球,那么抓到的期望就是这个概率,那么两个都扔出去的话,我们流经的费用之和是第一个抓到,第二个随便的概率加上第二个抓到,第一个随便的概率。我们重复计算的部分是两个都抓到的部分。于是我们就可以开始建图了。建图的方法是源点向两种球连流量为每种球个数,费用为 的边。然后每种球向每个神奇宝贝连流量是 ,费用为概率的边,表示一种球只能对一种神奇宝贝扔一次,抓到的概率就是给出的概率。然后对于每个神奇宝贝,我们向汇点连两条边。第一条边是流量为 ,费用为 的边,表示第一次通过时增加的量就是用某个球抓到这个神奇宝贝的概率。第二条边是流量为 ,费用为两个球都抓到这个神奇宝贝的概率乘积的相反数。这样第二条边流量是非正的,于是最大费用最大流的时候一定不会出现先经过它而出错的情况(如果是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;
}