[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 }