[luoguP2770] 航空路线问题(最小费用最大流)

传送门

 

模型

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

实现

为了限制经过次数,将每个点i拆成xi,yi.

1、从xi向yi连一条容量为1,费用为1的有向边(1<i<N),

2、从x1向y1连一条容量为2,费用为1的有向边,

3、从xN向yN连一条容量为2,费用为1的有向边,

4、如果存在边(i,j)(i<j)从yi向xj连一条容量为1,费用为0的有向边.

如果存在边(i,j)(i>j),那么交换i,j,再连边,因为原题是过去再回来,我们要转换成网络流的图,只求起点到终点的最大费用流。

如果存在边(i,j)(i==1 && j==n) 那么要连一条容量为2,费用为0的边。

因为原题要求最大费用流,所以我们需要将费用取反,求出费用后再取反回来。

求x1到yN的最大费用最大流.若(x1,y1)满流,则有解,答案为最大费用最大流-2;否则,无解.

分析

每条航线都是自西向东,本题可以转化为求航线图中从1到N两条不相交的路径,使得路径长度之和最大。转化为网络流模型,就是找两条最长的增广路。由于每个城市只能访问一次,要把城市

拆成两个点,之间连接一条容量为1的边,费用设为1。因为要找两条路径,所以起始点和终点内部的边容量要设为2。那么费用流值-2就是两条路径长度之和,为什么减2,因为有两条容量为2

的边多算了1的费用。求最大费用最大流后,如果(<1.a>,<1.b>)不是满流,那么我们找到的路径不够2条(可能是1条,也可能0条),所以无解。

 

——代码

  1 #include <map>
  2 #include <queue>
  3 #include <cstdio>
  4 #include <cstring>
  5 #include <iostream>
  6 #define N 10001
  7 #define M 1000001
  8 #define min(x, y) ((x) < (y) ? (x) : (y))
  9 
 10 int n, m, cnt, flow, ans, s, t;
 11 std::string str[1001], s1, s2;
 12 std::map <std::string, int> p;
 13 int head[N], to[M], val[M], cost[M], next[M], dis[N], pre[N];
 14 bool vis[N];
 15 
 16 inline void add(int x, int y, int z, int c)
 17 {
 18     to[cnt] = y;
 19     val[cnt] = z;
 20     cost[cnt] = c;
 21     next[cnt] = head[x];
 22     head[x] = cnt++;
 23 }
 24 
 25 inline bool spfa()
 26 {
 27     int i, u, v;
 28     std::queue <int> q;
 29     memset(vis, 0, sizeof(vis));
 30     memset(pre, -1, sizeof(pre));
 31     memset(dis, 127 / 3, sizeof(dis));
 32     q.push(s);
 33     dis[s] = 0;
 34     while(!q.empty())
 35     {
 36         u = q.front(), q.pop();
 37         vis[u] = 0;
 38         for(i = head[u]; i ^ -1; i = next[i])
 39         {
 40             v = to[i];
 41             if(val[i] && dis[v] > dis[u] + cost[i])
 42             {
 43                 dis[v] = dis[u] + cost[i];
 44                 pre[v] = i;
 45                 if(!vis[v])
 46                 {
 47                     q.push(v);
 48                     vis[v] = 1;
 49                 }
 50             }
 51         }
 52     }
 53     return pre[t] ^ -1;
 54 }
 55 
 56 int main()
 57 {
 58     int i, j, d, now;
 59     scanf("%d %d", &n, &m);
 60     s = 1, t = n << 1;
 61     memset(head, -1, sizeof(head));
 62     for(i = 1; i <= n; i++)
 63     {
 64         std::cin >> str[i];
 65         p[str[i]] = i;
 66         if(i ^ 1 && i ^ n)
 67             add(i, i + n, 1, -1), add(i + n, i, 0, 1);
 68         else
 69             add(i, i + n, 2, -1), add(i + n, i, 0, 1);
 70     }
 71     for(i = 1; i <= m; i++)
 72     {
 73         std::cin >> s1 >> s2;
 74         if(p[s1] > p[s2]) std::swap(s1, s2); 
 75         if(p[s1] == 1 && p[s2] == n)
 76             add(p[s1] + n, p[s2], 2, 0), add(p[s2], p[s1] + n, 0, 0);
 77         else
 78             add(p[s1] + n, p[s2], 1, 0), add(p[s2], p[s1] + n, 0, 0);
 79     }
 80     while(spfa())
 81     {
 82         d = 1e9;
 83         for(i = pre[t]; i ^ -1; i = pre[to[i ^ 1]]) d = min(d, val[i]);
 84         for(i = pre[t]; i ^ -1; i = pre[to[i ^ 1]])
 85         {
 86             val[i] -= d;
 87             val[i ^ 1] += d;
 88         }
 89         flow += d;
 90         ans += dis[t] * d;
 91     }
 92     if(flow ^ 2)
 93     {
 94         printf("No Solution!");
 95         return 0;
 96     }
 97     printf("%d\n", -ans - 2);
 98     memset(vis, 0, sizeof(vis));
 99     vis[1] = vis[0] = 1;
100     std::cout << str[1] << std::endl;
101     for(i = head[s + n]; i ^ -1; i = next[i])
102         if(!val[i] && !vis[to[i]])
103         {
104             now = to[i];
105             while(!vis[now])
106             {
107                 vis[now] = 1;
108                 std::cout << str[now] << std::endl;
109                 for(j = head[now + n]; j ^ -1; j = next[j])
110                     if(!val[j] && !vis[to[j]])
111                     {
112                         now = to[j];
113                         break;
114                     }
115             }
116             break;
117         }
118     for(i = head[n]; i ^ -1; i = next[i])
119         if(!val[i ^ 1] && !vis[to[i] - n])
120         {
121             now = to[i] - n;
122             while(!vis[now])
123             {
124                 vis[now] = 1;
125                 std::cout << str[now] << std::endl;
126                 for(j = head[now]; j ^ -1; j = next[j])
127                     if(!val[j ^ 1] && !vis[to[j] - n])
128                     {
129                         now = to[j] - n;
130                         break;
131                     }
132             }
133             break;
134         }
135     std::cout << str[1] << std::endl;
136     return 0;
137 }
View Code

 

posted @ 2017-06-03 14:36  zht467  阅读(397)  评论(0编辑  收藏  举报