POJ 2404 Jogging Trails

【题目大意】

      给你n个点,m条无向边,每条边有一定的距离数值,构造成一个连通图。问从任意一点出发,遍历所有的边,每条边至少访问一次,再回到起点,求满足要求的方案中走过的距离之和的最小短值。

【分析】

   首先想到的是如果这是一个欧拉图,那肯定能经过每条边有且仅有一次,这样的方案一定是最小的(所有边距离的和)。如果不是欧拉图,由于是连通图,根据握手定理,则必有偶数个点的度为奇数。要从一点出发每边至少走一次,则必须要构成一个欧拉回路,所以有些边必须要走多次,每多走一次等价多连接了一条边,这样构成欧拉图,原先的边和新加的虚拟边在欧拉图中有且仅经过一次。现在还要使距离之和最短。原先的边的距离之和是固定的了,要使结果最小,只能使新加的虚拟边之和最小。通过分析可以发现要构成欧拉图,添加的虚拟边的两个端点原先的度数一定是奇数,如果其中有偶数度的点,添加一边后度数就会变奇数,不可能成为欧拉图或者多此一举。于是现在的问题是如何在偶数个奇度顶点中两两连线,使得这些连线的距离之和最小,易想到两个顶点的连线长度应该是这两点间的最短距离(贪心)。想要解决这个问题,可以使用最优匹配算法,也可以使用动态规划。

    这里我使用动态规划的方法。

    状态表示:

         用一串二进制数,第i位数表示第i个点是否为奇度点,0表示不是,1表示是。例如00110101表示1、3、5、6点的度数为奇数。
         每个状态划分为一个阶段。

    阶段状态转移:

         每个状态可以从当前状态任意使两个1变为0 的状态转移而来,也就是说从删除一条边变为当前状态的状态转移而来。
         比如说00110101可以从6个状态转移而来:00000101、00110000、00010001、00100001、00010100、00100100

    无后效应:

         如果当前状态是通过之前的一条转移路径转移而来,不会导致之后有些本该转移的状态不可转移。
         例如:当前为00110101,无论之前如何转移,之后一定可以转移成00111111

    最优子结构:

         当前状态储存的值为在当前状态的情况下所需要的最少距离,这个值的转移方程为:

               f[cur]=min{f[pre]+dis[pre][cur]} (要求:pre状态可以转移到cur状态,dis[pre][cur]为删除的虚拟边的距离) 

【代码】

使用压缩状态动归的记忆化搜索算法

 1 #include <stdio.h>
 2 #include <string.h>
 3 const int maxn=0xffffff;
 4 int map[20][20];
 5 int du[20],dp[1<<16];
 6 int n,m;
 7 int sum;
 8 inline int min(int a,int b)
 9 {
10     return a>b?b:a;
11 }
12 void insert(int x,int y,int d)
13 {
14     if (d<map[x][y]) map[x][y]=map[y][x]=d;
15     sum+=d;
16     ++du[x];
17     ++du[y];
18 }
19 void floyed()
20 {
21     for (int k=0; k<n; ++k)
22         for  (int i=0; i<n; ++i)
23             for  (int j=0; j<n; ++j)
24             {
25                 if (i==j||j==k||i==k) continue;
26                 map[i][j]=min(map[i][j],map[i][k]+map[k][j]);
27             }
28 }
29 int search(int st)
30 {
31     if (st==0) return 0;
32     if (dp[st]) return dp[st];
33     int ans=maxn,tem;
34     for (int i=0; i<n-1; ++i)
35         if (st & (1<<i))
36             for (int j=i+1; j<n; ++j)
37             {
38                 if ((1<<j)&st)
39                 {
40                     tem=search(st-(1<<i)-(1<<j))+map[i][j];
41                     ans=min(ans,tem);
42                 }
43 
44             }
45     return dp[st]=ans;
46 }
47 int main()
48 {
49     while (~scanf("%d%d",&n,&m) && n)
50     {
51         sum=0;
52         memset(du,0,sizeof du);
53         memset(dp,0,sizeof dp);
54         for (int i=0; i<n; ++i)
55             for (int j=0; j<n; ++j) map[i][j]=maxn;
56         int x,y,z;
57         while (m--)
58         {
59             scanf("%d%d%d",&x,&y,&z);
60             insert(x-1,y-1,z);
61         }
62         floyed();
63         int st=0;
64         for (int i=0; i<n; ++i)
65             if (du[i]%2==1) st|=(1<<i);
66         sum+=search(st);
67         printf("%d\n",sum);
68     }
69 
70 }

 

posted @ 2013-05-06 22:53  wuminye  阅读(753)  评论(0编辑  收藏  举报