题意很显然,但是要注意product是有乘积的意思,即这次比较特殊的是不是求权值和最小,而是权值积最小。
思路:
1.权值积最小记得要用对数来处理。
2.熟悉二分图最大匹配的就可以看出其实这题是个二分图最小权值点覆盖。
二分图的X是行,Y是列,如果有伞兵(i,j)即有响应的x集合的点到y集合的点的边。
幸运的是,这些边没有权值,(即在下面构造的在流网络中权值为inf)。
但不能因此直接用最大匹配算法。
因为每个点有权值。
3.所以这里要用到最大流最小割的原理,设定源点和汇点。源点连接X集合的每个点的边的权值为X集合每个点的权值,同理汇点连接Y集合。
这样就转化为一个典型的流网络。
下面是代码,但是一直wa,可能是精度有问题,改天再改了。
#include<iostream>
#include<cmath>
using namespace std;
const int N=105,M=610;
const double inf=999999;
int r,c,l;
struct Edge
{
int v,next,re;
double w;
}edge[2*M];
int edgehead[N];
int k=1;
int final;
int level[N];
bool visit[N];
int que[N];
void addedge(int u,int v,double w)
{
edge[k].v=v;
edge[k].w=w;
edge[k].next=edgehead[u];
edge[k].re=k+1;
edgehead[u]=k++;
edge[k].v=u;
edge[k].w=0.0;
edge[k].next=edgehead[v];
edge[k].re=k-1;
edgehead[v]=k++;
}
bool bfs()
{
memset(visit,0,sizeof(visit));
memset(level,0,sizeof(level));
int head=1,tail=1;
visit[0]=true;
que[tail++]=0;
level[0]=0;
while(head<tail)
{
int now=que[head++];
if(now==final)
return true;
for(int i=edgehead[now];i;i=edge[i].next)
{
int v=edge[i].v;
if(!visit[v]&&edge[i].w)
{
visit[v]=true;
que[tail++]=v;
level[v]=level[now]+1;
}
}
}
return false;
}
double dinic(int now,double sum)
{
if(now==final)
return sum;
double os=sum;
for(int i=edgehead[now];i&∑i=edge[i].next)
{
int v=edge[i].v;
if(level[v]==level[now]+1&&edge[i].w)
{
double tmp=dinic(v,(edge[i].w>sum?sum:edge[i].w));
edge[i].w-=tmp;
edge[edge[i].re].w+=tmp;
sum-=tmp;
}
}
return os-sum;
}
void solve()
{
double ans=0.0;
while(bfs())
{
ans+=dinic(0,inf);
}
printf("%.4lf\n",exp(ans));
}
int main()
{
int cases;
scanf("%d",&cases);
while(cases--)
{
k=1;
memset(edge,0,sizeof(edge));
memset(edgehead,0,sizeof(edgehead));
scanf("%d%d%d",&r,&c,&l);
final=r+c+1;
for(int i=1;i<=r;i++)
{
double w;
scanf("%lf",&w);
addedge(0,i,log(w));
}
for(int i=1;i<=c;i++)
{
double w;
scanf("%lf",&w);
addedge(r+i,final,log(w));
}
for(int i=1;i<=l;i++)
{
int a,b;
scanf("%d%d",&a,&b);
addedge(a,r+b,inf);
}
solve();
}
return 0;
}