TonyYin's Blog

Back

题意#

KK 架飞机,NN 个机场,其中 11 号为基地机场。

每天 00 时刻,飞机才可以从基地机场起飞,并且不晚于 TT 时刻回到基地机场。

对于 i,j\forall i, j,题目给定 ti,jt_{i, j} 表示从 ii 空载飞到 jj 需要的时间,fi,jf_{i, j} 表示从 ii 空载飞到 jj 需要的费用。

MM包机请求,给定 (a,b,s,t,c)(a, b, s, t, c),表示在 ss 时刻从 aa 机场起飞,可以恰好在 tt 时刻到达 bb 机场,净获利 cc.

注意,包机请求的飞行时间为 (ts)ta,b(t-s)\neq t_{a, b}. 包机请求的费用为 c-cff 不同

求最大总收益。

分析#

不难发现,可以使用费用流算法。

对请求进行拆点。起点向终点连容量为 11,价值为 cc 的边。

然后处理时间限制。对于每个请求,如果:

  • **可以在请求开始之前,从基地飞到起点:**从源点向请求起点连边,容量为 inf\inf,价值为对应的 f-f.

  • **可以在 TT 之前,从终点飞到基地:**从终点向汇点连边,容量为 inf\inf,价值为对应的 f-f.

又因为执行请求之后,可以飞到别的请求机场。所以两两枚举请求,如果从终点飞到另一个请求的起点满足时间条件,就连边

处理 kk 架飞机。再建立一个源点,向原来的源点连容量为 kk,费用为 00 的边即可。

最大费用最大流

代码#

#include <bits/stdc++.h>

using namespace std;
const int MAXN = 408, MAXM = 1e6;
const int inf = 0x3f3f3f3f;
int n, m, k, ed;
int Tim[MAXN][MAXN], Cos[MAXN][MAXN];
int S, T;
struct Edge{
    int from, to, nxt, flow, val;
}edge[MAXM];
int head[MAXN], e_cnt = 1;
void add(int u, int v, int w, int c) {
    edge[++e_cnt].from = u;
    edge[e_cnt].to = v;
    edge[e_cnt].flow = w;
    edge[e_cnt].val = c;
    edge[e_cnt].nxt = head[u];
    head[u] = e_cnt;
}
void add_edge(int u, int v, int w, int c) {
    add(u, v, w, c);
    add(v, u, 0, -c);
}
struct In{int a, b, s, t, c;} in[MAXN];
int dis[MAXN], pre[MAXN];
bool vis[MAXN];
bool SPFA() {
    memset(dis, 0x3f, sizeof(dis)); dis[S] = 0;
    memset(vis, 0, sizeof(vis)); vis[S] = true;
    memset(pre, 0, sizeof(pre));
    queue<int> q; q.push(S);
    while(!q.empty()) {
        int x = q.front(); q.pop(); vis[x] = false;
        for(int i = head[x]; i; i = edge[i].nxt) {
            int v = edge[i].to, flow = edge[i].flow, val = edge[i].val;
            if(flow && dis[x] + val < dis[v]) {
                dis[v] = dis[x] + val;
                pre[v] = i;
                if(!vis[v]) {
                    q.push(v); vis[v] = true;
                }
            }
        }
    }
    return (pre[T] != 0);
}
pair<int, int> MCMF() {
    int flow = 0, cost = 0;
    while(SPFA()) {
        int minflow = inf;
        for(int i = T; i != S; i = edge[pre[i]].from) {
            minflow = min(minflow, edge[pre[i]].flow);
        }
        flow += minflow;
        for(int i = T; i != S; i = edge[pre[i]].from) {
            edge[pre[i]].flow -= minflow;
            edge[pre[i] ^ 1].flow += minflow;
            cost += edge[pre[i]].val * minflow;
        }
    }
    return make_pair(flow, cost);
}
int main() {
    scanf("%d%d%d%d", &n, &m, &k, &ed);
    S = 0; T = 2 * m + 2;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            scanf("%d", &Tim[i][j]);
        }
    }
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            scanf("%d", &Cos[i][j]);
        }
    }
    add_edge(S, S + 1, k, 0);
    for(int i = 1; i <= m; i++) scanf("%d%d%d%d%d", &in[i].a, &in[i].b, &in[i].s, &in[i].t, &in[i].c);
    for(int i = 1; i <= m; i++) {
        add_edge(i + 1, i + m + 1, 1, -in[i].c);
        if(in[i].t + Tim[in[i].b + 1][1] <= ed) add_edge(i + m + 1, T, inf, Cos[in[i].b + 1][1]);
        if(in[i].s - Tim[1][in[i].a + 1] >= 0) add_edge(S + 1, i + 1, inf, Cos[1][in[i].a + 1]);
        for(int j = 1; j <= m; j++) {
            if(in[i].t + Tim[in[i].b + 1][in[j].a + 1] <= in[j].s) {
                add_edge(i + m + 1, j + 1, inf, Cos[in[i].b + 1][in[j].a + 1]);
            }
        }
    }
    cout << -MCMF().second << endl;
    return 0;
}
c
【洛谷-P4452】[国家集训队]航班安排
https://www.tonyyin.top/blog/oi-solution/p4452
Author TonyYin
Published at July 14, 2021
Comment seems to stuck. Try to refresh?✨