zoukankan      html  css  js  c++  java
  • POJ-2135 Farm Tour---最小费用最大流模板题(构图)

    题目链接:

    https://vjudge.net/problem/POJ-2135

    题目大意:

    主人公要从1号走到第N号点,再重N号点走回1号点,同时每条路只能走一次。
    这是一个无向图。输入数据第一行是2个是N和M。N为点的数量,M为路径个数。
    接下来M行是边的数据,每行输入3个数,边的两个端点a,b和边的长度v。
    要你输出来回最短的路径长度。
    题目确保存在来回的不重复路径

    解题思路:

    这题可以转换成网络流的费用流。

    来回并且路径不相同就相当于有用两条从1到N的路径。

    把路径长度当成网络流里面每个流的费用,流量都设置成1这样就代表每条路径只能使用1次。增加2个点,源点和汇点,因为来回,就把源点到1建立一条流,流量为2(来回)费用为0,同样N到汇点建立一条流,流量为2费用为0。(保证只有两条路从源点到汇点,就是答案的解)这样一个网络流就出来了。

    这里输入一条边要建4条边,首先建a->b的有向边,要同时建立反向边,再建b->a的有向边,一样建立反向边。

    其他的就是模板了

    注意:有可能会有这样一个问题,一条无向边拆分成两条有向边,有没有可能会把这两条有向边都走了呢,答案是否定的,因为求的是最小费用,如果一条边正向反向均走了一次,那么总流量为0,而且还有额外的费用,而我们算法的策略是每次都取最短路(也就是最小费用)找增广路,所以不可能找出费用为正数流量为0的情况,所以放心的敲模板吧。

     1 #include<iostream>
     2 #include<vector>
     3 #include<cstring>
     4 #include<cstdio>
     5 #include<queue>
     6 using namespace std;
     7 const int INF = 0x3f3f3f3f;
     8 const int maxn = 1000 + 10;
     9 struct edge
    10 {
    11     int u, v, c, f, cost;
    12     edge(int u, int v, int c, int f, int cost):u(u), v(v), c(c), f(f), cost(cost){}
    13 };
    14 vector<edge>e;
    15 vector<int>G[maxn];
    16 int a[maxn];//找增广路每个点的水流量
    17 int p[maxn];//每次找增广路反向记录路径
    18 int d[maxn];//SPFA算法的最短路
    19 int inq[maxn];//SPFA算法是否在队列中
    20 int n, m;
    21 void init(int n)
    22 {
    23     for(int i = 0; i <= n; i++)G[i].clear();
    24     e.clear();
    25 }
    26 void addedge(int u, int v, int c, int cost)
    27 {
    28     e.push_back(edge(u, v, c, 0, cost));
    29     e.push_back(edge(v, u, 0, 0, -cost));
    30     int m = e.size();
    31     G[u].push_back(m - 2);
    32     G[v].push_back(m - 1);
    33 }
    34 bool bellman(int s, int t, int& flow, long long & cost)
    35 {
    36     for(int i = 0; i <= n + 1; i++)d[i] = INF;//Bellman算法的初始化
    37     memset(inq, 0, sizeof(inq));
    38     d[s] = 0;inq[s] = 1;//源点s的距离设为0,标记入队
    39     p[s] = 0;a[s] = INF;//源点流量为INF(和之前的最大流算法是一样的)
    40 
    41     queue<int>q;//Bellman算法和增广路算法同步进行,沿着最短路拓展增广路,得出的解一定是最小费用最大流
    42     q.push(s);
    43     while(!q.empty())
    44     {
    45         int u = q.front();
    46         q.pop();
    47         inq[u] = 0;//入队列标记删除
    48         for(int i = 0; i < G[u].size(); i++)
    49         {
    50             edge & now = e[G[u][i]];
    51             int v = now.v;
    52             if(now.c > now.f && d[v] > d[u] + now.cost)
    53                 //now.c > now.f表示这条路还未流满(和最大流一样)
    54                 //d[v] > d[u] + e.cost Bellman 算法中边的松弛
    55             {
    56                 d[v] = d[u] + now.cost;//Bellman 算法边的松弛
    57                 p[v] = G[u][i];//反向记录边的编号
    58                 a[v] = min(a[u], now.c - now.f);//到达v点的水量取决于边剩余的容量和u点的水量
    59                 if(!inq[v]){q.push(v);inq[v] = 1;}//Bellman 算法入队
    60             }
    61         }
    62     }
    63     if(d[t] == INF)return false;//找不到增广路
    64     flow += a[t];//最大流的值,此函数引用flow这个值,最后可以直接求出flow
    65     cost += (long long)d[t] * (long long)a[t];//距离乘上到达汇点的流量就是费用
    66     for(int u = t; u != s; u = e[p[u]].u)//逆向存边
    67     {
    68         e[p[u]].f += a[t];//正向边加上流量
    69         e[p[u] ^ 1].f -= a[t];//反向边减去流量 (和增广路算法一样)
    70     }
    71     return true;
    72 }
    73 int MincostMaxflow(int s, int t, long long & cost)
    74 {
    75     cost = 0;
    76     int flow = 0;
    77     while(bellman(s, t, flow, cost));//由于Bellman函数用的是引用,所以只要一直调用就可以求出flow和cost
    78     return flow;//返回最大流,cost引用可以直接返回最小费用
    79 }
    80 int main()
    81 {
    82     cin >> n >> m;
    83     int u, v, c;
    84     for(int i = 0; i < m; i++)
    85     {
    86         cin >> u >> v >> c;
    87         addedge(u, v, 1, c);
    88         addedge(v, u, 1, c);
    89     }
    90     int s = 0, t = n + 1;
    91 
    92     addedge(s, 1, 2, 0);//超级源点,边可通过两次,所以流量设成2,费用为0
    93     addedge(n, t, 2, 0);//超级汇点,边可通过两次,流量设成2,费用为0
    94     long long ans;
    95     MincostMaxflow(s, t, ans);
    96     cout<<ans<<endl;
    97     return 0;
    98 }
  • 相关阅读:
    影视感悟
    缩写字母
    从工程文化和运维理念理解Netflix
    telinit:Did not receive a reply.Possible causes include:the remote application did not send a reply, the message bus security policy blocked the reply, the reply timeout expired
    centos6 ext4修复
    windows显示日期时间(精确到秒)
    【C#】IDispose接口的应用
    【转】【WPF】WPF 自定义快捷键命令(Command)
    【转】【WPF】MVVM模式的3种command
    【转】【WPF】WriteableBitmap应用及图片数据格式转换
  • 原文地址:https://www.cnblogs.com/fzl194/p/8860649.html
Copyright © 2011-2022 走看看