给出一个矩阵,要求以最低花费覆盖掉矩阵中的所有点,覆盖规则是,可以一次选择一整行覆盖,或者一整列覆盖,(覆盖每行,每列都对应的有不同的花费),最终的花费是这些行和列花费的乘积。
建图:
这里因为要求的是乘积,不是加和,所以需要用log( a )+log( b ) = log( a * b )转化;
对于矩阵中的一个需要覆盖的点A,覆盖该点所在的行的花费是 wr ,覆盖该点所在列的花费是 wc;把点 A 按行标和列标拆成 X 和 Y 两个点,从源点连一条到 X 的边,权值为log( wr ),X 和 Y 之间连一条权值为无穷大的边,Y 到汇点连一条权值为 log( wc )的边,求最大流即可。
PS:由于网络流增广路的性质,每次增加的流是增广路上所有边权值中的最小值,这个题,对于每个点我们都有两种选择,覆盖该点所在行(所以有边s -> X),或者覆盖该点所在列(边 Y -> t);然后每个点只需要覆盖一次(边X -> Y);这就是建图的思想。
代码:
#include<cstdio> #include<cstring> #include<queue> #include<cmath> #define find_min(a,b) a<b?a:b using namespace std; const int N = 105; const double MAX = 1000000.0; struct Edge{ int s,e,next; double v; }edge[15*N]; int n,e_num,head[N],d[N],sp,tp; void AddEdge(int a,int b,double c){ edge[e_num].s=a; edge[e_num].e=b; edge[e_num].v=c; edge[e_num].next=head[a]; head[a]=e_num++; edge[e_num].s=b; edge[e_num].e=a; edge[e_num].v=0.0; edge[e_num].next=head[b]; head[b]=e_num++; } int bfs(){ queue <int> q; memset(d,-1,sizeof(d)); d[sp]=0; q.push(sp); while(!q.empty()){ int cur=q.front(); q.pop(); for(int i=head[cur];i!=-1;i=edge[i].next){ int u=edge[i].e; if(d[u]==-1 && edge[i].v>0){//没有标记,且可行流大于0 d[u]=d[cur]+1; q.push(u); } } } return d[tp] != -1;//汇点是否成功标号,也就是说是否找到增广路 } double dfs(int a,double b){//a为起点 double r=0; if(a==tp)return b; for(int i=head[a];i!=-1 && r<b;i=edge[i].next){ int u=edge[i].e; if(edge[i].v>0 && d[u]==d[a]+1){ double x=find_min(edge[i].v,b-r); x=dfs(u,x); r+=x; edge[i].v-=x; edge[i^1].v+=x; } } if(!r)d[a]=-2; return r; } double dinic(int sp,int tp){ double total=0.0,t; while(bfs()){ while(t=dfs(sp,MAX)) total+=t; } return total; } int main() { int t,i,m,l,a,b; double row[N],col[N]; scanf("%d",&t); while(t--) { scanf("%d%d%d",&n,&m,&l); for(i=1;i<=n;i++) scanf("%lf",&row[i]); for(i=1;i<=m;i++) scanf("%lf",&col[i]); e_num=0; memset(head,-1,sizeof(head)); sp=0; tp=n+m+1; for(i=1;i<=n;i++) AddEdge(sp,i,log(row[i])); for(i=1;i<=m;i++) AddEdge(n+i,tp,log(col[i])); for(i=1;i<=l;i++){ scanf("%d%d",&a,&b); AddEdge(a,n+b,MAX); } double ans=dinic(sp,tp); printf("%.4lf\n",exp(ans)); } return 0; }