题意翻译
有n 个人去执行n 个任务,每个人执行每个任务有不同的成功率,每个人只能执行一个任务,求所有任务都执行的总的成功率。
输入第一行,一个整数n (1≤n≤20 ),表示人数兼任务数。接下来n 行每行n 个数,第i 行第j个数表示第i 个人去执行第j 个任务的成功率(这是一个百分数,在0 到100 间)。
输出最大的总成功率(这应也是一个百分数)
输入输出样例
输入 #1复制
2
100 100
50 50
输出 #1复制
50.000000
输入 #2复制
2
0 50
50 0
输出 #2复制
25.00000
输入 #3复制
3
25 60 100
13 0 50
12 70 90
输出 #3复制
9.10000
可以状压dp或者爆搜,但是复杂度太高,虽然能过。
最简单的解法就是费用流。这里是相乘的最大费用流,我们先取log转化为相加的费用流,然后再取指数即可。
但是要特判流量不为n的情况,必然无解。
这里费用流用了dfs多路增广。
AC代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const double eps=1e-8;
const int N=1e2+10,M=1e4+10;
int n,g[N][N],vis[N],st[N],s,t,maxflow; double w[M],d[N],res;
int head[N],nex[M],to[M],flow[M],tot=1;
inline void ade(int a,int b,int c,double d){
to[++tot]=b; nex[tot]=head[a]; head[a]=tot; w[tot]=d; flow[tot]=c;
}
inline void add(int a,int b,int c,double d){ade(a,b,c,d); ade(b,a,0,-d);}
inline int spfa(){
for(int i=s;i<=t;i++) d[i]=1e5; queue<int> q; q.push(s);
d[s]=0; vis[s]=1; memset(st,0,sizeof st);
while(q.size()){
int u=q.front(); q.pop(); vis[u]=0;
for(int i=head[u];i;i=nex[i]){
if(flow[i]&&d[to[i]]>d[u]+w[i]){
d[to[i]]=d[u]+w[i];
if(!vis[to[i]]) vis[to[i]]=1,q.push(to[i]);
}
}
}
return d[t]!=(1e5);
}
int dfs(int x,int f){
if(x==t) return res+=d[t]*f,maxflow+=f,f;
st[x]=1; int fl=0;
for(int i=head[x];i&&f;i=nex[i]){
if(!st[to[i]]&&flow[i]&&fabs(d[to[i]]-d[x]-w[i])<eps){
int mi=dfs(to[i],min(flow[i],f));
flow[i]-=mi,flow[i^1]+=mi,fl+=mi,f-=mi;
}
}
return fl;
}
signed main(){
cin>>n; t=n*2+1;
for(int i=1;i<=n;i++){
add(s,i,1,0); add(i+n,t,1,0);
for(int j=1;j<=n;j++){
cin>>g[i][j];
if(g[i][j]) add(i,j+n,1,-log(g[i][j]*0.01));
}
}
while(spfa()) dfs(s,1e9);
if(res>eps&&maxflow==n) printf("%lf\n",exp(-res)*100);
else puts("0.000000");
return 0;
}