题目大意:有$n$个人有$n$个任务,每个人执行每个任务有不同的成功率,每个人只能执行一个任务,求所有任务都执行的总的成功率。
题解:可以跑最大费用最大流,把成功率取个$log$,最后$exp$回去就好了。
卡点:1.费用流边流量修改写挂
2.为判断无解情况($exp(0)==1$)
3.题目中没有卡一个任务没有人可以完成或者一个人什么任务都完不成的情况(原做法是状压)
C++ Code:
#include <cstdio>
#include <cmath>
#define maxn 50
using namespace std;
const int inf = 0x3f3f3f3f;
int n;
int H[maxn];
int head[maxn], cnt = 2;
struct Edge {
int to, nxt, w;
double cost;
} e[maxn * maxn << 1];
void add(int a, int b, int c, double d) {
e[cnt] = (Edge) {b, head[a], c, d}; head[a] = cnt;
e[cnt ^ 1] = (Edge) {a, head[b], 0, -d}; head[b] = cnt ^ 1;
cnt += 2;
}
int st, ed, MF;
double d[maxn];
bool inq[maxn];
int q[maxn << 2], h, t;
int pre[maxn];
inline double min(double a, double b) {return a < b ? a : b;}
bool spfa() {
for (int i = st; i <= ed; i++) d[i] = -inf;
d[q[h = t = 0] = st] = 0;
while (h <= t) {
int u = q[h++];
inq[u] = false;
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if (e[i].w && d[v] < d[u] + e[i].cost) {
d[v] = d[u] + e[i].cost;
pre[v] = i;
if (!inq[v]) {
inq[v] = true;
q[++t] = v;
}
}
}
}
return d[ed] > -inf / 10;
}
double update() {
int mf = inf;
for (int i = pre[ed]; i; i = pre[e[i ^ 1].to]) mf = min(mf, e[i].w);
for (int i = pre[ed]; i; i = pre[e[i ^ 1].to]) e[i].w -= mf, e[i ^ 1].w += mf;
MF += mf;
return mf * d[ed];
}
void MCMF() {
double ans = 0;
while (spfa()) ans += update();
if (MF != n) puts("0");
else printf("%.6lf\n", exp(ans) * 100.0);
}
int main() {
scanf("%d", &n);
st = 0, ed = n << 1 | 1;
for (int i = 1; i <= n; i++) {
add(st, i, 1, 0);
int tmp = 0;
for (int j = 1; j <= n; j++) {
int a;
scanf("%d", &a);
tmp += a;
H[j] += a;
add(i, j + n, 1, log(a / 100.0));;
}
add(i + n, ed, 1, 0);
}
MCMF();
return 0;
}