ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P2770 航空路线问题(最大费用最大流)

2021-04-13 21:33:45  阅读:323  来源: 互联网

标签:最大 int 路径 路线 maxn str P2770 起点 dis


传送门

题意:给一个图,一个起点,一个终点,要找到一条从起点到终点再回到起点的一条回路,

且每个点除起点只经过一次,且路径最长。

博客传送门

【题目描述】

给出一张有向图,每个点(除了起点 1)每条边都只能经过一次,求出从 1 到 n 在回到 1 的一条路径,使得经过的点个数最大,并输出路径。

【数据范围】

100\%100% n \leqslant 100n⩽100


【分析】

这是一道网络瘤题目。

那么,如何建模呢?

【建模】

俗话说得好啊:网络瘤,网络瘤,网络建模最毒瘤。 稍微一不注意踩到了坑就 GG 。

把题意转换一下,实际上是求从 11 到 nn 的两条互不相交的路径。 限制条件是除起点、终点外的每条边、每个点只能经过一次,那么就可以进行拆点,把点可以经过的最大次数作为点内部的流量,节点数作为点内部的费用,最后用 MCMF求一个最大流最大花费。

求出的最大流就是所找到的路径数,如果它小于等于 11,就说明找不到这样一条路径。 但有一种特殊情况需要特判:起点、终点只有一条边相连,这时候虽然只能找到一条路径,可 1 能直通 n 并直接回来,是合法的路径。

然后就是 美 (sang)(sang) 妙 (xin)(xin) 绝 (bing)(bing) 伦 (kuang)(kuang) 的建图了:

首先把每个点拆为入点和出点,并连一条流量(可经过次数)为 1,费用(节点数)为 1 的边,起点、终点要单独拆,流量设为 2 。

而对于输入的边 (x,y),要把 x的出点与 y的入点相连(其实是个很简单的道理,一开始死活想不明白,而大佬们的题解基本都没讲,可能是觉得太简单了吧,我太蒻了。。。)。

【求答案】

答案分三种情况:

(1).当最大流等于 2 时,最大花费减 2 即为可经过的最大节点数,减 2 是因为起点、终点都经过了两次。

(2).当处于上述 1直通 n 的情况时,答案为 2,路径为:起点→终点→起点。

(3). 其他情况均为无解。

另外,情况 (1) 中输出路径时写两个 dfs 遍历满流边:

第一次随便跑,记录下跑过的节点编号,要注意的是这时候只能跑出一条路,所以找到路后要立马 break。

第二次跳过这些节点找剩下那条路径,由于最大流(所找到的路径数)最多为 2,所以这里是否 break 都无所谓(可能会快一丢丢吧)。

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 600005;
const int inf = 0x3f3f3f3f;
struct edge {
    int f, t, nxt;
    ll flow,w;
}e[maxn * 2];
int hd[maxn], tot = 1;
void add(int f, int t, ll flow,ll w) {
    e[++tot] = { f,t,hd[f],flow ,w};
    hd[f] = tot;
    e[++tot] = { t,f,hd[t],0,-w};
    hd[t] = tot;
}
int s, t;
int n, m;
int dis[maxn], pre[maxn];
ll maxflow, mincost;
ll cyf[maxn];
bool inque[maxn];
bool spfa() {
    for (int i = 1; i <= 2*n; i++) {
        dis[i] = -inf; inque[i] = 0;
    }
    dis[s] = 0, inque[s] = 1, cyf[s] = inf;
    queue<int>q;
    q.push(s);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        inque[u] = 0;
        for (int i = hd[u]; i; i = e[i].nxt) {
            int v = e[i].t;
            if (e[i].flow > 0 && dis[u] + e[i].w > dis[v]) {
                pre[v] = i;
                cyf[v] = min(cyf[u], e[i].flow);
                dis[v] = dis[u] + e[i].w;
                if (!inque[v]) {
                    inque[v] = 1;
                    q.push(v);
                }
            }
        }
    }
    return dis[t] != -inf;
}
void mcmf() {
    while (spfa()) {
        int x = t; maxflow += cyf[t]; mincost += dis[t] * cyf[t];
        while (x != s) {
            int i = pre[x];
            e[i].flow -= cyf[t];
            e[i ^ 1].flow += cyf[t];
            x = e[i].f;
        }
    }
}
string str[105];
map<string, int>M;
bool vis[maxn];
string a, b;
void dfs1(int u) {
    vis[u-n] = 1;
    printf("%s\n", str[u-n].c_str());
    for (int i = hd[u]; i; i = e[i].nxt) {
        int v = e[i].t;
        if (v<=n && !e[i].flow) {
            dfs1(v + n); break;
        }
    }
}
void dfs2(int u) {
    vis[u - n] = 1;
    for (int i = hd[u]; i; i = e[i].nxt) {
        int v = e[i].t;
        if (v <= n && !vis[v] && !e[i].flow) { dfs2(v + n); break; }
    }
    printf("%s\n", str[u-n].c_str());
}
int main() {
    //freopen("test.txt", "r", stdin);
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        cin >> str[i];
        M[str[i]] = i;
        if (i != 1 && i != n)add(i, i + n, 1, 1);//经过点费用为1
        else add(i, i + n, 2, 1);//起点和终点的容量设置为2
    }
    int flg = 0;
    for (int i = 1; i <= m; i++) {
        cin >> a >> b;
        int x = M[a], y = M[b];
        if (x > y)swap(x, y);//只从左向右连
        flg|= (x == 1&&y == n);//判断是否起点和终点直接有边相连,这种可能是可以的。
        add(x + n, y, 1, 0);//两点连边费用为0
    }
    s = 1, t = 2*n;
    mcmf();//最大花费最大流
    if (maxflow==2) {
        printf("%lld\n", mincost - 2);
    }
    else if(flg&&maxflow==1){
        printf("2\n");
        printf("%s\n", str[1].c_str());
        printf("%s\n", str[n].c_str());
        printf("%s\n", str[1].c_str());
        return 0;
    }
    else {
        printf("No Solution!\n");
        return 0;
    }
    dfs1(1 + n);//先找一条路径,并标记路径上的点
    dfs2(1 + n);//再找一条
    return 0;
}

 

标签:最大,int,路径,路线,maxn,str,P2770,起点,dis
来源: https://www.cnblogs.com/MYMYACMer/p/14655425.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有