当前位置: 代码迷 >> 综合 >> POJ 3308 最小割最优点权覆盖
  详细解决方案

POJ 3308 最小割最优点权覆盖

热度:91   发布时间:2024-01-20 20:25:52.0

题目大意:

火星人入侵地球,他们降落的区域为一个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;
}