【CF739E】Gosha is hunting

题目

题目链接:https://codeforces.com/problemset/problem/739/E
你要抓神奇宝贝!
现在一共有 N N N 只神奇宝贝。
你有 a a a 个『宝贝球』和 b b b 个『超级球』。
『宝贝球』抓到第 i i i 只神奇宝贝的概率是 p i p_i pi ,『超级球』抓到的概率则是 u i u_i ui
不能往同一只神奇宝贝上使用超过一个同种的『球』,但是可以往同一只上既使用『宝贝球』又使用『超级球』(都抓到算一个)。
请合理分配每个球抓谁,使得你抓到神奇宝贝的总个数期望最大,并输出这个值。
n ≤ 2000 n\leq 2000 n2000

思路

考虑建立费用流模型。首先对于宝贝球和超级球分别建立一个点,从源点连一条流量为球的数量,费用为 0 0 0 的边来限制每一个球的数量。
然后对于一个点 i i i,从宝贝球的点连一条 ( 1 , p i ) (1,p_i) (1,pi) 的边,从超级球连一条 ( 1 , u i ) (1,u_i) (1,ui) 的边,然后从 i i i 向汇点连一条 ( 1 , 0 ) (1,0) (1,0) 的边,再连一条 ( 1 , − p i × u i ) (1,-p_i\times u_i) (1,pi×ui) 的边。然后跑最大费用最大流。如果这个逼只用了一个球来抓,那么就会优先流费用为 0 0 0 的边,否则两条边都流,贡献即为 p i + u i − p i u i p_i+u_i-p_iu_i pi+uipiui,恰好是抓到它的期望。
时间复杂度 O ( n 3 ) O(n^3) O(n3)

代码

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

const int N=2010,M=20010,Inf=1e9;
const double eps=1e-10;
int n,a,b,A,B,S,T,tot,pre[N],head[N];
double cost,p[2][N],dis[N];
bool vis[N];

struct edge
{
	int next,to,flow;
	double cost;
}e[M];

void add(int from,int to,int flow,double cost)
{
	e[++tot]=(edge){head[from],to,flow,cost};
	head[from]=tot;
	swap(from,to);
	e[++tot]=(edge){head[from],to,0,-cost};
	head[from]=tot;
}

bool spfa()
{
	memset(dis,0xcf,sizeof(dis));
	queue<int> q;
	q.push(S); dis[S]=0;
	while (q.size())
	{
		int u=q.front(); q.pop();
		vis[u]=0;
		for (int i=head[u];~i;i=e[i].next)
		{
			int v=e[i].to;
			if (e[i].flow && dis[v]<dis[u]+e[i].cost-eps)
			{
				dis[v]=dis[u]+e[i].cost; pre[v]=i;
				if (!vis[v]) { vis[v]=1; q.push(v); }
			}
		}
	}
	return dis[T]>0;
}

void addflow()
{
	int minf=Inf;
	for (int i=T;i!=S;i=e[pre[i]^1].to)
		minf=min(minf,e[pre[i]].flow);
	for (int i=T;i!=S;i=e[pre[i]^1].to)
	{
		e[pre[i]].flow-=minf;
		e[pre[i]^1].flow+=minf;
	}
	cost+=dis[T]*minf;
}

void MCMF()
{
	while (spfa()) addflow();
}

int main()
{
	memset(head,-1,sizeof(head));
	tot=1; S=N-1; T=N-2; A=N-3; B=N-4;
	scanf("%d%d%d",&n,&a,&b);
	for (int i=1;i<=n;i++) scanf("%lf",&p[0][i]);
	for (int i=1;i<=n;i++) scanf("%lf",&p[1][i]);
	for (int i=1;i<=n;i++)
	{
		add(A,i,1,p[0][i]); add(B,i,1,p[1][i]);
		add(i,T,1,0); add(i,T,1,-p[0][i]*p[1][i]);
	}
	add(S,A,a,0); add(S,B,b,0);
	MCMF();
	printf("%.10lf",cost);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值