[网络流24题]P4013 数字梯形
费用流,拆点,一般题。
https://www.luogu.com.cn/problem/P4013
题意
有一个由数字堆成的梯形,每到一个数字就可以获得相对应的分数,问在以下三种规则能分别得到的最高分
从梯形的顶至底的 m 条路径互不相交;
从梯形的顶至底的 m 条路径仅在数字结点处相交;
从梯形的顶至底的 m 条路径允许在数字结点相交或边相交。
Turotial
第一问拆点,其他问只要将一些边改成inf即可,需要注意第二问时也需要修改和 \(T\) 相邻的边,因为可以在末尾处汇合。
将图复原的简便操作:
for (int i = 0; i < cnt_edge; i += 2) e[i].flow += e[i ^ 1].flow, e[i ^ 1].flow = 0;
写到这里不禁有一个疑问,为什么不可以直接加边,然后继续增广呢?我这样做得到的答案是0,很是不解。看题解好像也都是重新建图的。
点击查看代码
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <vector>
#define endl '\n'
#define IOS \
ios::sync_with_stdio(0); \
cin.tie(0); \
cout.tie(0);
#define P pair<int, int>
using namespace std;
#define int long long
typedef long long ll;
const int maxn = 20 * 40 * 2 * 2 + 5;
const ll inf = 1e18;
int n1, n2, cnt_edge = 1, S, T;
int a1[maxn], a2[maxn], head[maxn];
ll dis[maxn];
bool vis[maxn];
struct edge {
int to, nxt;
ll flow, cost;
} e[(maxn * maxn) << 2];
inline void add(int u, int v, ll w, ll c) {
e[++cnt_edge].nxt = head[u];
head[u] = cnt_edge;
e[cnt_edge].to = v;
e[cnt_edge].flow = w;
e[cnt_edge].cost = c;
}
inline void addflow(int u, int v, ll w, ll c) {
add(u, v, w, c);
add(v, u, 0, -c);
// if(u + 1 == v)
// cout << "add: "<<u << " " << v << " "<<cnt_edge<<endl;
}
inline bool spfa(int on) {
memset(vis, 0, sizeof(vis));
if (on == 1)
for (int i = 0; i <= T; i++) dis[i] = inf;
else
for (int i = 0; i <= T; i++) dis[i] = -inf;
queue<int> q;
q.push(S);
dis[S] = 0;
vis[S] = 1;
while (!q.empty()) {
int x = q.front();
q.pop();
vis[x] = 0;
for (int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
// cout << "->" << y << endl;
if ((on == 1 && e[i].flow && dis[y] > dis[x] + e[i].cost) ||
(on == -1 && e[i].flow && dis[y] < dis[x] + e[i].cost)) {
dis[y] = dis[x] + e[i].cost;
if (!vis[y]) q.push(y), vis[y] = 1;
}
}
}
if (on == 1)
return dis[T] != inf;
else
return dis[T] != -inf;
}
ll dfs(int x, ll lim) {
vis[x] = 1;
if (x == T || lim <= 0) return lim;
ll res = lim;
for (int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if (dis[y] != dis[x] + e[i].cost || e[i].flow <= 0 || vis[y]) continue;
ll tmp = dfs(y, min(res, e[i].flow));
res -= tmp;
e[i].flow -= tmp;
e[i ^ 1].flow += tmp;
if (res <= 0) break;
}
return lim - res;
}
inline ll Dinic(int on) {
ll res = 0, cost = 0;
while (spfa(on)) {
ll flow = dfs(S, inf);
res += flow, cost += flow * dis[T];
}
return cost;
}
int val[maxn][maxn];
int id(int x, int y, int in) {
return 2 * ((n1 * 2 + x - 2) * (x - 1) / 2 + y - 1) + in;
}
vector<int> edge[3];
//拆点之间的边
//除S T 的相邻的边
//T相邻边
signed main() {
cin >> n1 >> n2;
S = (2 * n1 + n2 - 1) * n2 + 1, T = (2 * n1 + n2 - 1) * n2 + 2;
for (int i = 1; i <= n2; i++) {
for (int j = 1; j <= n1 + i - 1; j++) {
cin >> val[i][j];
addflow(id(i, j, 0), id(i, j, 1), 1, val[i][j]);
edge[0].push_back(cnt_edge);
//
}
}
for (int i = 1; i <= n1; i++) addflow(S, id(1, i, 0), 1, 0);
for (int i = 1; i <= n1 + n2 - 1; i++)
addflow(id(n2, i, 1), T, 1, 0), edge[2].push_back(cnt_edge);
for (int i = 1; i < n2; i++) {
for (int j = 1; j <= n1 + i - 1; j++) {
addflow(id(i, j, 1), id(i + 1, j, 0), 1, 0);
edge[1].push_back(cnt_edge);
addflow(id(i, j, 1), id(i + 1, j + 1, 0), 1, 0);
edge[1].push_back(cnt_edge);
}
}
cout << Dinic(-1) << endl;
// for (int i = 1; i <= n2; i++) {
// for (int j = 1; j <= n1 + i - 1; j++) {
// addflow(id(i, j, 0), id(i, j, 1), inf, val[i][j]);
// // edge[0].push_back(cnt_edge);
// //
// }
// }
for (int i = 0; i < cnt_edge; i += 2)
e[i].flow += e[i ^ 1].flow, e[i ^ 1].flow = 0;
for (auto i : edge[0]) {
e[i ^ 1].flow = inf;
}
for (auto i : edge[2]) {
e[i ^ 1].flow = inf;
}
cout << Dinic(-1) << endl;
for (int i = 0; i < cnt_edge; i += 2)
e[i].flow += e[i ^ 1].flow, e[i ^ 1].flow = 0;
for (auto i : edge[1]) {
// cout << (i ^ 1) << " " << e[i ^ 1].to << endl;
// cout << i << " " << e[i].to << endl;
e[i ^ 1].flow = inf;
}
cout << Dinic(-1) << endl;
return 0;
}

浙公网安备 33010602011771号