[网络流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;
}
posted @ 2021-10-21 15:34  FushimiYuki  阅读(42)  评论(0)    收藏  举报