POJ3308 Paratroopers 最大流最小割

题意很显然,但是要注意product是有乘积的意思,即这次比较特殊的是不是求权值和最小,而是权值积最小。

思路:

1.权值积最小记得要用对数来处理。

2.熟悉二分图最大匹配的就可以看出其实这题是个二分图最小权值点覆盖。

二分图的X是行,Y是列,如果有伞兵(i,j)即有响应的x集合的点到y集合的点的边。

幸运的是,这些边没有权值,(即在下面构造的在流网络中权值为inf)。

但不能因此直接用最大匹配算法。

因为每个点有权值。

3.所以这里要用到最大流最小割的原理,设定源点和汇点。源点连接X集合的每个点的边的权值为X集合每个点的权值,同理汇点连接Y集合。

这样就转化为一个典型的流网络。


下面是代码,但是一直wa,可能是精度有问题,改天再改了。

#include<iostream>
#include<cmath>
using namespace std;
const int N=105,M=610;
const double inf=999999;
int r,c,l;
struct Edge
{
	int v,next,re;
	double w;
}edge[2*M];
int edgehead[N];
int k=1;
int final;
int level[N];
bool visit[N];
int que[N];
void addedge(int u,int v,double w)
{
	edge[k].v=v;
	edge[k].w=w;
	edge[k].next=edgehead[u];
	edge[k].re=k+1;
	edgehead[u]=k++;
	edge[k].v=u;
	edge[k].w=0.0;
	edge[k].next=edgehead[v];
	edge[k].re=k-1;
	edgehead[v]=k++;
}
bool bfs()
{
	memset(visit,0,sizeof(visit));
	memset(level,0,sizeof(level));
	int head=1,tail=1;
	visit[0]=true;
	que[tail++]=0;
	level[0]=0;
	while(head<tail)
	{
		int now=que[head++];
		if(now==final)
			return true;
		for(int i=edgehead[now];i;i=edge[i].next)
		{
			int v=edge[i].v;
			if(!visit[v]&&edge[i].w)
			{
				visit[v]=true;
				que[tail++]=v;
				level[v]=level[now]+1;
			}
		}
	}
	return false;
}
double dinic(int now,double sum)
{
	if(now==final)
		return sum;
	double os=sum;
	for(int i=edgehead[now];i&&sum;i=edge[i].next)
	{
		int v=edge[i].v;
		if(level[v]==level[now]+1&&edge[i].w)
		{
			double tmp=dinic(v,(edge[i].w>sum?sum:edge[i].w));
			edge[i].w-=tmp;
			edge[edge[i].re].w+=tmp;
			sum-=tmp;
		}
	}
	return os-sum;
}
void solve()
{
	double ans=0.0;
	while(bfs())
	{
		ans+=dinic(0,inf);
	}
	printf("%.4lf\n",exp(ans));
}
int main()
{
	int cases;
	scanf("%d",&cases);
	while(cases--)
	{
		k=1;
		memset(edge,0,sizeof(edge));
		memset(edgehead,0,sizeof(edgehead));
		scanf("%d%d%d",&r,&c,&l);
		final=r+c+1;
		for(int i=1;i<=r;i++)
		{
			double w;
			scanf("%lf",&w);
			addedge(0,i,log(w));
		}
		for(int i=1;i<=c;i++)
		{
			double w;
			scanf("%lf",&w);
			addedge(r+i,final,log(w));
		}
		for(int i=1;i<=l;i++)
		{
			int a,b;
			scanf("%d%d",&a,&b);
			addedge(a,r+b,inf);
		}
		solve();
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值