题意翻译
有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;
}