【线性规划与网络流24题】8-11 航空路线问题

Description

给定一张航空图,图中顶点代表城市,边代表2城市间的直通航线。现要求找出一条满足下述限制条件的且途经城市最多的旅行路线。

(1)从最西端城市出发,单向从西向东途经若干城市到达最东端城市,然后再单向从东向西飞回起点(可途经若干城市)。

(2)除起点城市外,任何城市只能访问1次。

对于给定的航空图,试设计一个算法找出一条满足要求的最佳航空旅行路线。

Input Format

第1 行有2个正整数N 和V,N 表示城市数,N<100,V 表示直飞航线数。接下来的N行中每一行是一个城市名,可乘飞机访问这些城市。城市名出现的顺序是从西向东。也就是说,设i,j 是城市表列中城市出现的顺序,当i>j 时,表示城市i 在城市j 的东边,而且不会有2 个城市在同一条经线上。城市名是一个长度不超过15 的字符串,串中的字符可以是字母或阿拉伯数字。例如,AGR34或BEL4。

再接下来的V 行中,每行有2 个城市名,中间用空格隔开,如 city1 city2 表示city1到city2 有一条直通航线,从city2 到city1 也有一条直通航线。

Output Format

输出最佳航空旅行路线。第1 行是旅行路线中所访问的城市总数M。接下来的M+1 行是旅行路线的城市名,每行写1 个城市名。首先是出发城市名,然后按访问顺序列出其它城市名。注意,最后1行(终点城市)的城市名必然是出发城市名。如果问题无解,则输出“No Solution!”。

Sample Input

8 9
Vancouver
Yellowknife
Edmonton
Calgary
Winnipeg
Toronto
Montreal
Halifax
Vancouver Edmonton
Vancouver Calgary
Calgary Winnipeg
Winnipeg Toronto
Toronto Halifax
Montreal Halifax
Edmonton Montreal
Edmonton Yellowknife
Edmonton Calgary

Sample Output

7
Vancouver
Edmonton
Montreal
Halifax
Toronto
Winnipeg
Calgary
Vancouver

分析:
  从最西端的城市到最东端的城市,再回到最西端的城市,可以看作是两次从最西端的城市到最东端的城市,就是寻找最长两条不相交路径,用费用流解决。
  构图:
    (1)因为每个城市只能经过一次,把城市拆成两个点Ai和Bi,Ai向Bi连一条流量为1,费用为0的边。
    (2)从源点连向B1,流量为2,费用为0;从An连向汇点,流量为2,费用为0。
    (3)从Bi向Aj连一条流量为1,费用为-1的边,表示城市i与城市j相通。如果i = 1, j = n,那么流量为2。
  在图上跑一遍最小费用最大流(因为费用变成了负数,如果是正数就是最大费用最大流),然后取相反数就是结果。
  如果源点连向B1的边的流量大于0,就说明无法找到第二条增广路,则输出"
No Solution!"。
  输出答案的时候,根据流量图里的信息,另外构建一个图,表示所有城市之间的联通性,如果Bi到Aj的边流量为0,则城市i和j联通。然后在图上做一次搜索把城市名称输出就好了。

代码:
  1 #include <cstdio>
  2 #include <cstring>
  3 const int maxn = 500;
  4 const int maxm = 500000;
  5 const int maxq = 5000000;
  6 const int inf = 2147483647;
  7 char name[110][20], s1[20], s2[20];
  8 int n, m, a, b, s, t, en, el[maxn];
  9 int et[maxm], ep[maxm], ef[maxm], ec[maxm];
 10 int q[maxq], h, r, now, to, pn[maxn], pe[maxn], d[maxn], v[maxn];
 11 inline void ins (int sta, int end, int fl, int co) //邻接表,网络流使用
 12 {
 13     et[++en] = end; ep[en] = el[sta]; ec[en] = co; ef[en] = fl; el[sta] = en;
 14     et[++en] = sta; ep[en] = el[end]; ec[en] = -co; ef[en] = 0; el[end] = en;
 15 }
 16 int ot[maxm], op[maxm], on, ol[maxn], ov[maxn];
 17 inline void ins2 (int sta, int end) //邻接表2,输出结果时使用
 18 {
 19     ot[++on] = end; op[on] = ol[sta]; ol[sta] = on;
 20     ot[++on] = sta; op[on] = ol[end]; ol[end] = on;
 21 }
 22 inline int min (int arg0, int arg1)
 23 {
 24     return arg0 < arg1 ? arg0 : arg1;
 25 }
 26 bool bfs () //寻找增广路
 27 {
 28     memset (v, 0, sizeof (v));
 29     memset (d, 63, sizeof (d));
 30     memset (pe, -1, sizeof (pe));
 31     memset (pn, -1, sizeof (pn));
 32     d[q[h = r = 0] = s] = 0, v[t] = 1;
 33     while (h <= r)
 34     {
 35         v[now = q[h++]] = 0;
 36         for (int i = el[now]; i; i = ep[i])
 37         {
 38             if (d[to = et[i]] > d[now] + ec[i] && ef[i])
 39             {
 40                 d[to] = d[now] + ec[i];
 41                 pn[to] = now; pe[to] = i;
 42                 if (!v[to]) v[q[++r] = to] = 1;
 43             }
 44         }
 45     }
 46     return pe[t] != -1;
 47 }
 48 void dfs (int x) //Dinic
 49 {
 50     printf ("%s\n", name[x]); ov[x] = 1;
 51     for (int i = ol[x]; i; i = op[i])
 52         if (!ov[ot[i]]) dfs (ot[i]);
 53 }
 54 void work ()
 55 {
 56     int ans = 0, tmp;
 57     while (bfs ()) //最小费用最大流
 58     {
 59         tmp = inf;
 60         for (int i = t; i ^ s; i = pn[i])
 61             tmp = min (tmp, ef[pe[i]]);
 62         for (int i = t; i ^ s; i = pn[i])
 63         {
 64             ef[pe[i]] -= tmp;
 65             ef[pe[i] ^ 1] += tmp;
 66         }
 67         ans -= tmp * d[t];
 68     }
 69     if (ef[2]) printf ("No Solution!\n"); //ef[2]为第一条边的剩余流量,如果还有剩余,则说明没有找到两条增广路
 70     else
 71     {
 72         memset (ov, 0, sizeof (ov));
 73         printf ("%d\n", ans);
 74         for (int i = n + 1; i < s; i++)
 75             for (int j = el[i]; j; j = ep[j])
 76                 if (et[j] <= n && et[j] != i - n && ef[j] == 0)
 77                     ins2 (i - n, et[j]); //构建新图
 78         dfs (1); printf("%s\n", name[1]);
 79     }
 80 }
 81 int main ()
 82 {
 83     scanf ("%d %d", &n, &m); en = 1;
 84     s = n * 2 + 1; t = s + 1;
 85     ins (s, 1 + n, 2, 0); ins (n, t, 2, 0); //构图
 86     for (int i = 1; i <= n; i++)
 87     {
 88         scanf ("%s", name[i]);
 89         ins (i, i + n, 1, 0); //构图
 90     }
 91     for (int i = 0; i < m; i++)
 92     {
 93         scanf ("%s %s", s1, s2);
 94         a = b = 0;
 95         for (int j = 1; j <= n; j++)
 96         {
 97             strcmp (name[j], s1) ? 0 : a = j;
 98             strcmp (name[j], s2) ? 0 : b = j;
 99         } //暴力判定字符串
100         ins (a + n, b, 1, -1); //构图
101         if (a == 1 && b == n)
102             ins (n + 1, n, 1, -1);
103     }
104     work ();
105 }

因为网络上没有找到Special Judge的代码,就自己写了一个(用Cena测评):
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 
 5 FILE *fscore, *freport, *fstd, *fin, *fout;
 6 int cnt[110][110], vis[110];
 7 char name[110][20], s1[20], s2[20];
 8 char res1[20], res2[20];
 9 
10 bool Judge ()
11 {
12     int n, m;
13     fscanf (fstd, "%s", res1);
14     fscanf (fout, "%s", res2);
15     if (strcmp (res1, res2)) return false;
16     if (!strcmp (res1, "No"))
17     {
18         fscanf (fout, "%s", res2);
19         return !strcmp (res2, "Solution!");
20     }
21     fscanf (fin, "%d %d", &n, &m);
22     for (int i = 1; i <= n; i++)
23         fscanf (fin, "%s", name[i]);
24     int a, b;
25     for (int i = 0; i < m; i++)
26     {
27         fscanf (fin, "%s %s", s1, s2);
28         a = b = 0;
29         for (int j = 1; j <= n; j++)
30         {
31             strcmp (name[j], s1) ? 0 : a = j;
32             strcmp (name[j], s2) ? 0 : b = j;
33         }
34         cnt[a][b] = cnt[b][a] = 1;
35     }
36     int c = 0;
37     for (int i = 0; res1[i] >= '0' && res1[i] <= '9'; i++)
38         c = c * 10 + res1[i] - '0';
39     for (int i = 0; i <= c; i++)
40     {
41         fscanf (fout, "%s", s1);
42         a = 0;
43         for (int j = 1; j <= n; j++)
44             strcmp (name[j], s1) ? 0 : a = j;
45         if (a == 0) return false; vis[a]++;
46         if (i == 0) if (a != 1) return false;
47         if (i == c) if (a != 1) return false;
48         if (i > 0) if (cnt[a][b] == 0) return false;
49         b = a;
50     }
51     if (vis[1] != 2) return false;
52     if (vis[n] != 1) return false;
53     for (int i = 2; i < n; i++)
54         if (vis[i] > 1) return false;
55     return true;
56 }
57 
58 int main (int argc, char *argv[])
59 {
60     int score = atoi (argv[1]);
61     fscore = fopen ("score.log", "w");
62     freport = fopen ("report.log", "w");
63     fstd = fopen (argv[2], "r");
64     fin = fopen ("airl.in", "r");
65     fout = fopen ("airl.out", "r");
66     if (!fout)
67     {
68         fprintf (fscore, "%d", 0);
69         fprintf (freport, "No Output");
70     } else if (Judge ())
71     {
72         fprintf (fscore, "%d", score);
73         fprintf (freport, "Right");
74     } else
75     {
76         fprintf (fscore, "%d", 0);
77         fprintf (freport, "Wrong");
78     }
79     fclose (fscore);
80     fclose (freport);
81     return 0;
82 }

 

posted @ 2015-03-06 12:10  Lightning34  阅读(299)  评论(2编辑  收藏  举报