给出一个矩阵,要求以最低花费覆盖掉矩阵中的所有点,覆盖规则是,可以一次选择一整行覆盖,或者一整列覆盖,(覆盖每行,每列都对应的有不同的花费),最终的花费是这些行和列花费的乘积。
建图:
这里因为要求的是乘积,不是加和,所以需要用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;
}