题意:n*m的地图,给出L个火星人登陆的坐标,要在火星人登陆地球的瞬间全部消灭他们,有一种激光枪,一次可以消灭一行(或一列),消灭一行(或一列)有不同的代价,总代价是所有激光枪的代价之积。
思路:之前做过类似的题是求最少多少次能消灭,而最少的次数不一定是代价最小的,行跟列建立二分图,每个火星人就是一条边,就是选一些点覆盖所有的边,这些点的权值之积最小,如果是求和的话就是二分图的最小点权覆盖集了,所以要把求积转化成求和,a*b=log(a)+log(b);求出最小割就可以了。
#include<stdio.h> #include<math.h> #include<string.h> const int N=3000; #define inf 100.0 int head[N],num,ans,start,end,gap[N],dis[N]; struct edge { int st,ed,next; double flow; }e[N*100]; void addedge(int x,int y,double w) { e[num].st=x;e[num].ed=y;e[num].flow=w;e[num].next=head[x];head[x]=num++; e[num].st=y;e[num].ed=x;e[num].flow=0;e[num].next=head[y];head[y]=num++; } double dfs(int u,double minflow) { if(u==end)return minflow; int i,v; double f,flow=0.0; for(i=head[u];i!=-1;i=e[i].next) { v=e[i].ed; if(e[i].flow>0) { if(dis[v]+1==dis[u]) { f=dfs(v,e[i].flow>minflow-flow?minflow-flow:e[i].flow); flow+=f; e[i].flow-=f; e[i^1].flow+=f; if(minflow-flow<=1e-8)return flow; if(dis[start]>=ans)return flow; } } } if(--gap[dis[u]]==0) dis[start]=ans; dis[u]++; gap[dis[u]]++; return flow; } double isap() { double maxflow=0.0; memset(gap,0,sizeof(gap)); memset(dis,0,sizeof(dis)); gap[0]=ans; while(dis[start]<ans) maxflow+=dfs(start,inf); return maxflow; } int main() { int i,n,m,k,x,y,t; double w; scanf("%d",&t); while(t--) { scanf("%d%d%d",&n,&m,&k); memset(head,-1,sizeof(head)); num=0;start=0;end=n+m+1;ans=end+1; for(i=1;i<=n;i++) { scanf("%lf",&w); addedge(start,i,log(w)); } for(i=1;i<=m;i++) { scanf("%lf",&w); addedge(i+n,end,log(w)); } while(k--) { scanf("%d%d",&x,&y); addedge(x,y+n,inf); } w=isap(); printf("%.4lf\n",exp(w)); } return 0; }