P6054 [RC-02] 开门大吉 题解
首先我们可以简单求出第
如果没有约束条件,我们就可以贪心的对每个人选择期望收益最小的套题。
而为了将约束条件加进去,我们考虑使用网络流描述上面的贪心做法(本题使用
- 对每个人
i ,我们从源点到汇点建一条长度为m+2 的链,记(i,j) 表示第i 条链上的第j 个点(不含源点); - 对每个人
i ,建边\lang S,(i,1),\infty\rang ; - 对每个人
i 和每套题j ,建边\lang(i,j),(i,j+1),v_{i,j}\rang ; - 对每个人
i ,建边\lang (i,m+1),T,\infty\rang ;
这样建图后我们跑最小割,不难发现每条链都会割掉恰好一条边,且正好对应着那个人选的那套题。
那么怎么表示约束呢?我们假设存在一个约束
- 对一个限制
(i,j,k) ,对所有1\le d\le m+1-k ,连边\lang(j,d),(i,d+k),\infty\rang 。
注意到两组限制
注意到如果
解决这个问题的方法有许多种,这里给出最简单的一种方式:我们去掉
最后跑一遍最小割即可求出答案。
#include <iomanip>
#include <iostream>
#include <vector>
using namespace std;
const int kN = 82, kY = 1e3 + 1;
const double kEps = 1e-6, kI = 1e9;
struct E {
int y;
double w;
} e[kN * (kY + kN) * 2];
int tt, n, m, pc, cc, ca[kN];
double pa[kN], va[kN][kN], ans;
int sp, ep, np, ne, d[kN * kN];
vector<int> ed[kN * kN];
vector<int>::iterator it[kN * kN];
int q[kN * kN], qh, qt;
int E(int i, int j) {
return (i - 1) * (m + 1) + j;
}
void _A(int x, int y, double w) {
e[++ne] = {y, w};
ed[x].push_back(ne);
}
void A(int x, int y, double w) {
_A(x, y, w), _A(y, x, 0);
}
void R(int x, int _d) {
if (!d[x]) {
d[x] = _d, q[++qt] = x;
}
}
bool B() {
qh = 1, qt = 0;
fill_n(d + 1, np, 0);
for (R(sp, 1); qh <= qt; ++qh) {
int x = q[qh];
if (x == ep) {
return 1;
}
for (int i : ed[x]) {
if (e[i].w > kEps) {
R(e[i].y, d[x] + 1);
}
}
}
return d[ep];
}
double D(int x, double f) {
if (f < kEps) {
return 0;
}
if (x == ep) {
return f;
}
double rf = 0;
for (auto &i = it[x]; i != ed[x].end(); ++i) {
double iv = 0;
if (d[e[*i].y] == d[x] + 1 && (iv = D(e[*i].y, min(f - rf, e[*i].w))) > kEps) {
rf += iv, e[*i].w -= iv, e[*i ^ 1].w += iv;
if (abs(rf - f) < kEps) {
break;
}
}
}
return rf;
}
int main() {
ios_base::sync_with_stdio(0), cin.tie(0);
for (cin >> tt; tt--;) {
cin >> n >> m >> pc >> cc;
for (int i = 1; i <= pc; ++i) {
cin >> ca[i];
}
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
for (int k = 1; k <= pc; ++k) {
cin >> pa[k];
}
va[j][i] = 0;
double p = 1;
int s = 0;
for (int k = 1; k <= pc; ++k) {
p *= pa[k], s += ca[k];
va[j][i] += p * (1 - pa[k + 1]) * s;
}
}
}
sp = n * (m + 1) + 1, np = ep = n * (m + 1) + 2;
ne = 1;
for (int i = 1; i <= np; ++i) {
ed[i].clear();
}
for (int i = 1; i <= n; ++i) {
A(sp, E(i, 1), kI);
for (int j = 1; j <= m; ++j) {
A(E(i, j), E(i, j + 1), va[i][j]);
}
A(E(i, m + 1), ep, kI);
}
for (int i, j, k; cc--;) {
cin >> i >> j >> k;
for (int d = max(1, 1 - k); d <= m + 1; ++d) {
A(E(j, d), E(i, min(m + 1, d + k)), kI);
}
}
ans = 0;
for (; B();) {
for (int i = 1; i <= np; ++i) {
it[i] = ed[i].begin();
}
ans += D(sp, kI);
}
if (ans >= kI) {
cout << "-1\n";
} else {
cout << fixed << setprecision(4) << ans << '\n';
}
}
return 0;
}