【网络流24题】解题报告:K、航空路线问题(最小费用最大流)

在这里插入图片描述
【问题分析】

求最长两条不相交路径,用最大费用最大流解决。

【建模方法】

把第i个城市拆分成两个顶点<i.a>,<i.b>。

1、对于每个城市i,连接(<i.a>,<i.b>)一条容量为1,费用为1的有向边,特殊地(<1.a>,<1.b>)和(<N.a>,<N.b>)容量设为2。
2、如果城市i,j(j>i)之间有航线,从<i.b>到<j.a>连接一条容量为1,费用为0的有向边。

求源<1.a>到汇<N.b>的最大费用最大流。如果(<1.a>,<1.b>)不是满流,那么无解。否则存在解,即为最大费用最大流量 - 2。

【建模分析】

每条航线都是自西向东,本题可以转化为求航线图中从1到N两条不相交的路径,使得路径长度之和最大。转化为网络流模型,就是找两条最长的增广路。由于每个城市只能访问一次,要把城市拆成两个点,之间连接一条容量为1的边,费用设为1。因为要找两条路径,所以起始点和终点内部的边容量要设为2。那么费用流值-2就是两条路径长度之和,为什么减2,因为有两条容量为2的边多算了1的费用。求最大费用最大流后,如果(<1.a>,<1.b>)不是满流,那么我们找到的路径不够2条(可能是1条,也可能0条),所以无解。

【问题另解】

经典的多线程动态规划问题。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#include<map>
using namespace std;
typedef long long ll;
const int N = 500007, M = 500007, INF = 0x3f3f3f3f;

namespace dinic{
    
    
    const int N = 500007, M = 500007, INF = 0x3f3f3f3f;
    const ll LINF = 0x3f3f3f3f3f;
    int S, T, n;
    int head[N], nex[M], ver[M], tot, cur[N];
    ll dist[N], edge[M], cost[M], maxflow, mincost;
    bool vis[N];

    inline void add(int x, int y, ll z, ll c, bool o = 1){
    
    
        ver[tot] = y;
        edge[tot] = z;
        cost[tot] = c;
        nex[tot] = head[x];
        head[x] = tot ++ ;
        if(o)add(y, x, 0, -c, 0);
    }

    inline bool spfa(){
    
    
        for(int i = 1; i <= n; ++ i)dist[i] =LINF;
        memset(vis, 0, sizeof vis);
        queue<int>q;
        q.push(S);
        dist[S] = 0;
        vis[S] = 1;
        while(q.size()){
    
    
            int x = q.front();
            q.pop();
            vis[x] = 0;
            for(int  i = head[x]; ~i ;i = nex[i]){
    
    
                int y = ver[i];
                ll z = edge[i], c = cost[i];
                if(dist[y] > dist[x] + z && z){
    
    
                    dist[y] = dist[x] + z;
                    if(!vis[y])
                        q.push(y), vis[y] = 1;
                }
            }
        }
        return dist[T] != LINF;
    }
    ll dfs(int x, ll flow = LINF){
    
    
        if(x == T)return flow;
        ll ans = 0, k, i;
        vis[x] = 1;
        for(int i = cur[x]; ~i; i = nex[i]){
    
    
            int y = ver[i];
            ll z = edge[i], c = cost[i];
            if(z && dist[y] == dist[x] + c && vis[y]){
    
    
                k = dfs(y, min(z, flow));
                if(!k)dist[y] = LINF;
                edge[i] -= k;
                edge[i ^ 1] += k;
                ans += k, mincost += k * c, flow -= k;
            }
            vis[x] = 0;
            return ans;
        }
    }

    inline void main(){
    
    
        while(spfa()){
    
    
            for(int i = 1; i <= n; ++ i)
                cur[i] = head[i];
            ll now;
            while((now = dfs(S)))
                maxflow += now;
        }
    }
    inline void init(int _n, int _S, int _T){
    
    
        n = _n, S = _S, T = _T, tot = 0, maxflow = 0, mincost = 0;
        memset(head, -1, sizeof head);
    }
}
map<string, int>ip;
bool way[N];
string s[N], ch;
int n, m, S, T;
int x, y;

inline void dfs1(int x){
    
    
    way[x] = 1;
    cout <<s[x - n] << endl;
    for(int i = dinic::head[x]; ~i; i = dinic::nex[i]){
    
    
        int y = dinic::ver[i], z = dinic::edge[i];
        if(y <= n && z){
    
    
            dfs1(y + n);
            break;
        }
    }
}

inline void dfs2(int x){
    
    
    for(int i = dinic::head[x]; ~i; i = dinic::nex[i]){
    
    
        int y = dinic::ver[i], z = dinic::edge[i];
        if(y <= n && z && !way[y + n])
            dfs2(y + n);

    }
    cout << s[x - n] << endl;
}

int main(){
    
    
    scanf("%d%d", &n, &m);
    S = 1, T = n << 1;
    bool flag = 0;
    dinic::init(n + n + n, S, T);
    for(int i = 1; i <= n; ++ i)
        cin >> s[i], ip[s[i]] = i;
    //i ~ n 表示入点 n + 1 ~ 2 * n 表示出点
    for(int i = 2; i < n; ++ i)
        dinic::add(i, n + i, 1, 1);
        cout << "ok" << endl;
    dinic::add(1, n + 1, 2, 1), dinic::add(n, n + n, 2, 1);
    while(m -- ){
    
    
        cin >> ch, x = ip[ch];
        cin >> ch, y = ip[ch];
        if(x > y)swap(x, y);
        flag |= (x == 1 && y == n);
        dinic::add(x + n, y, 1, 0);
    }
    cout << "ok" << endl;
    dinic:main();
        cout << "ok" << endl;
    if(dinic::maxflow == 2)printf("%d", dinic::mincost - 2);
    else if(dinic::maxflow == 1 && flag){
    
    
        printf("2\n");
        cout << s[1] << endl << s[n] <<endl << s[1] << endl;
        return 0;
    }
    else return !printf("No Solution!\n");
    for(int i = 1;  i <= n + 2; ++ i)
        way[i + n] = 0;
    dfs1(1 + n), dfs2(1 + n);
}

猜你喜欢

转载自blog.csdn.net/weixin_45697774/article/details/108555522
今日推荐