POJ 3308 最小割最优点权覆盖

题目大意:

火星人入侵地球,他们降落的区域为一个n*m的矩形兵工厂,在这个区域行顶点和列顶点处有激光束,激光束一次能消灭一排或一列的敌人。为了消灭所有入侵的火星人,求最小的花费。每安装一个激光束都有一个权值,花费所有激光束权值的积。

思路:

如果不考虑到点权值,如POJ3041,每个点权值都为1,也就是求最大二分图匹配了。

用匈牙利或者网络流都很好做的。

so... 但是这题求的是最优点权覆盖,怎么做呢....

可以发现这题还是个二分图.. 

咱们把行定为[1,n]列点定为[n+1,n+m],这样构造二分图。

源点与行点连边,容量为行的点权,列点与汇点连边,容量为列的点权,做到限制流量。

边为行与列点连边,容量INF。

咱们要消灭所有的敌人。也就是在这个图中每条边的两顶点之一满流。

怎样保证两顶点之一满流呢?求最大流就OK了。

因为:如果有边e(u,v)的顶点u,v都没有满流,则一定可以增大流量。

为啥是两顶点之一满流?

因为当行点满流,则在该行上的所有敌人全部消灭。

当列点满流,则在该列上的所有敌人全部消灭。

所以要消灭相应敌人则必须是该敌人代表的边e(u,v)至少两顶点之一满流。

这里明明是+法,咋来的乘法??

我也没懂... 看网上的解法...

大概是这么个意思:

a*b*c*d=e^ln(a*b*c*d)=e^( ln(a)+ln(b)+ln(c)+ln(d) );

ln()在C++里是log()

e^x在C++里是exp()

综上为题解.....

code:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstdlib>
#include<cmath>
#define MN 111
#define CC(a) memset( a,0,sizeof(a) )
#define FF(i,a) for( int i=0;i<a;i++ )
#define INF 0x3FFFFFFF
#define eps 1e-7
using namespace std;

double maze[MN][MN];
int gap[MN],cur[MN],pre[MN],dis[MN];
int m,n,l,s,t;
double min( double a,double b ){ return a<b?a:b; }

void setG()
{
 	 FF(i,MN)FF(j,MN)
		 maze[i][j]=0;
 	 s=0,t=n+m+1;
 	 for( int i=1;i<=n;i++ )
 	 {
 	 	  scanf( "%lf",&maze[s][i] );
 	 	  maze[s][i]=log(maze[s][i]);
	 }
 	 for( int i=1;i<=m;i++ )
 	 {
 	 	  scanf( "%lf",&maze[i+n][t] );
 	 	  maze[i+n][t]=log(maze[i+n][t]);
	 }
 	 int u,v;
 	 FF( i,l )
 	 {
 	 	 scanf( "%d%d",&u,&v );
 	 	 maze[u][v+n]=INF;
	 }
}

double sap()
{
 	   CC(cur),CC(dis),CC(gap);
 	   int u=pre[s]=s;
 	   double maxflow=0,aug=INF;
 	   gap[0]=t+1;
 	   while( dis[s]<=t ){
loop:
	   		  for( int v=cur[u];v<=t;v++ )
	   		  if( maze[u][v]>0&&dis[u]==dis[v]+1 )
			  {
			   	  //printf( "%d",u );
			   	  //ge/char();
			   	  pre[v]=u;
			   	  cur[u]=v;
			   	  aug=min(aug,maze[u][v]);
			   	  u=v;
			   	  if( v==t )
			   	  {
				   	  maxflow+=aug;
				   	  //printf( "%lf!!",aug );getchar();
				   	  for( u=pre[u];v!=s;v=u,u=pre[u] )
				   	  	   maze[u][v]-=aug,maze[v][u]+=aug;
				   	  aug=INF;
  	  	   		  }
  	  	   		  goto loop;
	   	   	  }
	   	   	  int mind=t;
	   	   	  for( int v=0;v<=t;v++ )
	   	   	  if( maze[u][v]>0&&mind>dis[v] )
	   	   	  {
			   	  cur[u]=v;
			   	  mind=dis[v];
			  }
			  if( --gap[dis[u]]==0 )break;
			  gap[dis[u]=mind+1]++;
			  u=pre[u];
	   }
 	   return maxflow;
}

int main()
{
 	int T;
 	scanf( "%d",&T );
 	while( T-- )
 	{
	 	   scanf( "%d%d%d",&n,&m,&l );
	 	   setG();
	 	   double ans=sap();/*
	 	   double ans=1;
	 	   for( int i=1;i<=n;i++ )
	 	   		if( maze[i][s]>0 )
	 	   			ans*=maze[i][s];*/
	 	   printf( "%.4lf\n",exp(ans) );
  	}
 	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值