• 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 }
  • 相关阅读:
    Angular JS 中的内置方法之$watch
    Angular JS 中 指令详解
    Angular JS 中 ng-controller 值复制和引用复制
    git 使用技巧
    itextpdf 解析带中文的html问题
    详解Java 8中Stream类型的“懒”加载
    JSP网页处理过程
    [Java 8] (10) 使用Lambda完成函数组合,Map-Reduce以及并行化
    深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)
    Android:联系人Contacts之ContentResolver query 参数详解
  • 原文地址:https://www.cnblogs.com/wuminye/p/3063902.html
Copyright © 2020-2023  润新知