【问题分析】
求最长两条不相交路径,用最大费用最大流解决。
【建模方法】
把第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);
}