posts - 7,comments - 3,trackbacks - 0

 1871: Jogging Trails


ResultTIME LimitMEMORY LimitRun TimesAC TimesJUDGE
3s8192K5917Standard
Gord is training for a marathon. Behind his house is a park with a large network of jogging trails connecting water stations. Gord wants to find the shortest jogging route that travels along every trail at least once.

Input consists of several test cases. The first line of input for each case contains two positive integers: n <= 15, the number of water stations, and m < 1000, the number of trails. For each trail, there is one subsequent line of input containing three positive integers: the first two, between 1 and n, indicating the water stations at the end points of the trail; the third indicates the length of the trail, in cubits. There may be more than one trail between any two stations; each different trail is given only once in the input; each trail can be travelled in either direction. It is possible to reach any trail from any other trail by visiting a sequence of water stations connected by trails. Gord's route may start at any water station, and must end at the same station. A single line containing 0 follows the last test case.

For each case, there should be one line of output giving the length of Gord's jogging route.

Sample Input

4 5
1 2 3
2 3 4
3 4 5
1 4 10
1 3 12
0

Output for Sample Input

41





古老的图论问题:中国邮递员问题,曾经在某个图论书上看过,之后就没有之后了......
题目大意很简单:给一无向网络N,从点1出发,每条边至少进过一次后回到点1。
思路:(copy别人的)
1. 先解释下"度"的概念, 对于无向图中某结点, 它和n条边相连, 就称它的度为n. (有向图还分出度入度, 这里简化了, 不管)

2. 参考欧拉回路的概念, 无向图存在欧拉回路, 当前仅当所有点度数均为偶数. 
证明比较简单, 因为走完一条回路, 对于所有点均进去一次, 出来一次. 故, 对于任意点的度数,都是成对的在"消耗".

3. 题中所描述的回路, 有重复经过某边, 这没关系. 现在假设邮递员按题目要求走了一条最短的回路P.
那么, 把P所有重复经过的边, 拆开. 即假设三次经过边L, 则我们可以人为的在原图上多加两条边, 对于所有重复经过的边都这样做. 修改后的图记为G2. (原图为G)
对于G2, 邮递员走的回路, 即为此图的欧拉回路. 这是重点. (其实很好想, 因为G2中的每条边都仅被邮递员走过一次.)

4. 原问题就可以转化为, 如何把G添加一些边, 变成G2
而G2所有点度数均为偶数(这是肯定的, 因为G2存在欧拉回路)
故转化为, 如何把G中某些边复制x次, 从而消灭G中的奇点(度数为奇数的点).

5. 可能有点晕, 举个例子
图G只有两个点a和b, 一条边连着ab, 那么ab均为奇点, 要消灭掉, 就把ab之间连线复制一次(因为你不可能随便给它加一条不存在的..)
然后变成两条边连着ab. 从G2的角度看, 是欧拉图了, 对应成G, 就是把这条边走了两次而已.

6. 奇点总是成对的被消灭, 就算两个奇点不互通, 而是经过很多点才能连通, 那也要连出一条路径来, 以消灭这对奇点.

7. 那就好办了.把所有奇点找出来, 然后配对, 如果某种配对方式代价最小, 即为所求.
这里还要用上floyd, 求最短路

8. 配对算法也要比较好, 裸着配大概会超时, 这个懒得说了.. 如果不会, 直接看代码吧.

给点扼要注释, deg记录度数, mp记录最短路. u记录递归时各点是否已配对. (偶点直接被记为已配对. 递归时只路过)
dfs返回在当前此种u记录的配对情况下, 把未配对的匹配完, 最小代价是多少

还是提一句吧, 每次配对时, 配对中的第一个可以直接取队列中的第一个没有配对的元素(其实随便取都行). 第二个, 循环一次剩下的即可.
因为配对是不关心顺序的, 所以第一个可以想怎么取都行. 为了方便, 就第一个了.

代码:
#include <cstdio>
#include 
<cstring>
#include 
<cmath>
#include 
<iostream>
#define N 17
#define inf 1 << 20
using namespace std;

int deg[N], map[N][N], u[N];
int ans, n, m;

int min(int a, int b)
{
    
if (a > b) return b;
    
return a;
}

int slove()
{
    
int i, j, rnt = inf;
    
for (i = 0; i < n; ++i)
    
if (!u[i]) break;
    
if (i == n) return 0;
    u[i] 
= 1;
    
for (j = i + 1; j < n; ++j)
    {
        
if (!u[j]) u[j] = 1, rnt = min(rnt, slove() + map[i][j]), u[j] = 0;
    }
    u[i] 
= 0;
    
return rnt;
}

int main()
{
    
while (scanf("%d"&n) && n)
    {
        memset(u, 
0sizeof(u));
        memset(deg, 
0sizeof(deg));
        
for (int i = 0; i < n; ++i)
            
for (int j = 0; j < n; ++j)
            map[i][j] 
= inf;
        ans 
= 0;
        scanf(
"%d"&m);
        
for (int i = 1; i <= m; ++i)
        {
            
int a, b, c;
            scanf(
"%d%d%d"&a, &b, &c);
            ans 
+= c;
            a
--; b--;
            deg[a]
++;
            deg[b]
++;
            map[a][b] 
= map[b][a] = min(map[a][b], c);
        }
        
for (int k = 0; k < n; ++k)
            
for (int i = 0; i < n; ++i)
                
for (int j = 0; j < n; ++j)
                {
                    map[i][j] 
= min(map[i][j], map[i][k] + map[k][j]);
                }
        
for (int i = 0; i < n; ++i)
        
if (deg[i] % 2 == 0) u[i] = 1;
        printf(
"%d\n", ans + slove());
    }
    
return 0;
}
posted on 2011-10-15 22:13 LLawliet 阅读(243) 评论(0)  编辑 收藏 引用 所属分类: 图论

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理