POJ 3308 Paratroopers(最小割+Dinic)

题目链接:http://poj.org/problem?id=3308

题意:外星人来攻打地球了。。。外星人派了一些伞兵来攻占地球的兵工场,兵工厂是一个矩形网格,伞兵会降落在兵工厂的某个位置。地球人有激光武器,可以杀死一行或者一列的所有敌人。但是每个激光武器安置在每行每列的费用都是不同的。伞兵很厉害只要一个就可以消灭兵工厂,所以,现在要部署一套激光系统使得伞兵一降落就可以消灭所有敌人。并且要求费用最少。

题解:

每个伞兵要用安置在其行上或者其列上的激光武器消灭。将矩阵的每行每列都视为一个点,将源点与“行点”相连,将汇点与“列点”相连对于伞兵降落的某个位置,将其行点和列点连接。根据割的性质,源点与汇点必不连通,因此割边集中必定存在S->R、R->C、C->T 其一。为了求得最小容量,将R->C 的容量设为无穷大,则其不可能被选中。这样割边集为S->R 与C->T的集合,也就是选中了行或列。此时求得的最小割即为花费最小的方案。

注意:1.花费是权值的乘积,可以用log和exp转化为加的形式。2.double的精度问题,据说有个2被原则,即eps取保留位数的2倍,所以取1e-8。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
const int MAXN=500+50*2+10;
const double INF=1e8;
const double eps=1e-8;
double g[MAXN][MAXN];
int level[MAXN];
int cur[MAXN];
int N,S,E;

double precision(double x)
{
	return fabs(x)<eps?0:x;
}

int BFS()
{
	memset(level,-1,sizeof(level));
	level[S]=0;
	queue<int>que;
	que.push(S);
	while(!que.empty())
	{
		int u=que.front();
		que.pop();
		for(int v=0;v<N;v++)
		{
			if(level[v]<0&&precision(g[u][v])>0.0)
			{
				level[v]=level[u]+1;
				que.push(v);
			}
		}
	}
	if(level[E]>0) return 1;
	return 0;
}

double DFS(int x,double low)
{
	if(x==E) return low;
	for(int i=cur[x];i<N;i++)
	{
		cur[x]=i;
		if(precision(g[x][i])>0.0&&level[i]==level[x]+1)
		{
			double a=DFS(i,min(low,g[x][i]));
			if(precision(a)>0.0)
			{
				g[x][i]-=a;
				g[i][x]+=a;
				return a;
			}
		}
	}
	return 0;
}

double  Dinic()
{
	double ans=0;
	while(BFS())
	{
		memset(cur,0,sizeof(cur));
		double tans;
		while(tans=DFS(S,INF)) ans+=tans;
	}
	return ans;
}
int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	int tcase;
	scanf("%d",&tcase);
	while(tcase--)
	{
		int m,n,l;
		scanf("%d%d%d",&m,&n,&l);
		N=m+n+2;
		S=0;E=N-1;
		for(int i=0;i<N;i++)
		{
			for(int j=0;j<N;j++)
				g[i][j]=0.0;
		}
		for(int i=1;i<=m;i++)
		{
			double val;
			scanf("%lf",&val);
			g[S][i]=log(val);
		}
		for(int i=1;i<=n;i++)
		{
			double val;
			scanf("%lf",&val);
			g[m+i][E]=log(val);
		}
		for(int i=1;i<=l;i++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			g[x][m+y]=INF;
		}/*
		for(int i=0;i<N;i++)
		{
			for(int j=0;j<N;j++)
				cout<<g[i][j]<<" ";
			cout<<endl;
		}*/
		printf("%.4lf\n",exp(Dinic()));
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值