题目大意:
在一个n*m的格子地图上有l个外星人,有一种激光炮可以消灭他们,每当使用一次可以使某一行或某一列的外星人消失。但每使用一次都需要付一定的价钱。给出在每一行或每一列使用激光炮时需要的价格,问最少需要付多少才能消灭全部外星人。总价格是每一次使用价格的乘积。
解题思路:
这题看起来跟POJ3041差不多,但在这里增加了价格就不一样了。虽然建出来的图类似于一个二分图,但是在这里得用求最小割的办法来解决问题,也就建图的时候需要一个超级源点和超级汇点。
1、首先建图,超级汇点向坐标X依次建边,前向边的容量是在下表是X的那一行使用激光炮的价格取log(),(注1)后向边容量为零。同理建出向坐标Y到超级汇点的。
2、对于每一个外星人的坐标,建立相应X点到Y点的边,前向边容量无穷大,后向边容量为0。
3、然后使用Dinic算法求出最大流,也就是最小割容量。
4、用exp() 求出最小花费,
注意:
1、因为求最大流或者最小割是一个求和的过程,如果最小花费是乘积需要对数转化。
2、注意double的精度问题。
下面是代码:
#include <stdio.h> #include <string.h> #include <queue> #include <math.h> using namespace std; const int inf=1e8; const int Maxn=1005; const double eps = 0.00000001; struct node { int v,next; double w; } edge[ Maxn*5]; int n,m,l,head[Maxn],cnt,deep[Maxn]; void init() { memset(head,-1,sizeof(head)); cnt=0; } double Eps(double x) { return fabs(x)<eps?0:x; } double min(double a,double b) { return a<b?a:b; } void addedge(int u,int v,double w) { edge[cnt].v=v; edge[cnt].w=w; edge[cnt].next=head[u]; head[u]=cnt; cnt++; edge[cnt].v=u; edge[cnt].w=0; edge[cnt].next=head[v]; head[v]=cnt; cnt++; } bool bfs() { memset(deep,-1,sizeof(deep)); queue <int > q; q.push(0); deep[0]=0; while(!q.empty()) { int t=q.front(); q.pop(); int p=head[t]; while(p!=-1) { int v=edge[p].v; if( deep[v]==-1&&Eps(edge[p].w)>0) { q.push(v); deep[v]=deep[t]+1; } p=edge[p].next; } } return deep[n+m+1]!=-1; } void does(int u,int v,double w) { int p =head[u]; while(edge[p].v!=v) { p=edge[p].next; } edge[p].w+=w; } double dfs(int src,double flow) { if(src==n+m+1)return flow; double sum=0; int p=head[src]; while(p!=-1) { int v=edge[p].v; if(deep[v]==deep[src]+1&&Eps(edge[p].w)>0) { double tmp=dfs(v,min(flow-sum,edge[p].w)); sum+=tmp; edge[p].w-=tmp; does(v,src,tmp); } p=edge[p].next; } return sum; } double dinic() { double ans=0; while(bfs()) { ans+=dfs(0,inf); } return ans; } int main() { int T; scanf("%d",&T); while(T--) { double val; int u,v; init(); scanf("%d%d%d",&n,&m,&l); for(int i=1; i<=n; i++) { scanf("%lf",&val); addedge(0,i,log(val)); } for(int i=1; i<=m; i++) { scanf("%lf",&val); addedge(n+i,n+m+1,log(val)); } for(int i=0; i<l; i++) { scanf("%d%d",&u,&v); addedge(u,v+n,inf); } printf("%0.4f\n",exp(dinic())); } return 0; }