中国邮递员问题CPP

来源:互联网 发布:中国银行上海软件中心 编辑:程序博客网 时间:2024/05/01 12:22


问题描述:Chinese poster-man  problem,简称CPP,给出一张连通图,问经过每条边至少一次且起点和终点相同,所需走的最小路程。

无向图CPP

1.考虑当所有点度数均为偶数时,该图是欧拉图,因此任意一条欧拉回路都是答案

2.当有两个点是奇度点的时候,只需找到这两点间的最短路径,将最短路径上的边计入到原图中,这是得到了一张欧拉图(poj 1237 The Postal Worker Rings Once

3.当奇度点个数大于2时,需要使每个点的度数均变为偶数,即在2n个点之间连n条路,于是变为了无向图的最小匹配问题(KM算法时错误的,因为无法保证解的正确性,带花树开花算法是正确的),一般采用状压dp解决该问题。(poj 2404 Jogging Trails)

对于每个奇度点,用一个二进制位表示,1代表尚未匹配,0代表已经匹配,寻找当前状态下尚未匹配的两点匹配同时更新状态,然后利用记忆化搜索寻找最优解。

import java.io.IOException;import java.util.Arrays;import java.util.Scanner;public class Main {int dgr[] = new int[20];int map[][] = new int[20][20], inf = 1 << 28;Scanner scan = new Scanner(System.in);void floyd(int n) {for (int k = 1; k <=n; k++)for (int i = 1; i <=n; i++)for (int j = 1; j <=n; j++)map[i][j] = Math.min(map[i][j], map[i][k] + map[k][j]);}int stack[]=new int[20],top;int dp[]=new int[1<<17];int dfs(int s){if(s==0)return 0;if(dp[s]!=-1)return dp[s];int res=inf,i=0;while((s&(1<<i))==0) i++;for(int j=i+1;j<top;j++)if((s&(1<<j))!=0){int temp=dfs(s-(1<<i)-(1<<j))+map[stack[i]][stack[j]];res=Math.min(res, temp);}return dp[s]=res;}void run() {while (true) {int n = scan.nextInt();if (n == 0)break;int m = scan.nextInt();Arrays.fill(dgr, 0);for (int i = 0; i < 20; i++)Arrays.fill(map[i], inf);int sum = 0;while (m-- > 0) {int a = scan.nextInt();int b = scan.nextInt();int c = scan.nextInt();map[b][a]=map[a][b] = Math.min(map[a][b], c);sum += c;dgr[a]++;dgr[b]++;}floyd(n);top=0;for(int i=1;i<=n;i++)if(dgr[i]%2==1)stack[top++]=i;Arrays.fill(dp, -1);sum+=dfs((1<<top)-1);System.out.println(sum);}}public static void main(String[] args) throws IOException {new Main().run();}}

有向图CPP:

与无向图相同,都是试图用最小花费构造欧拉路,首先计算每个点的出度和入度的差d(v),然后利用最小费用流求解即可,构图如下:

1 其顶点集为图G 的所有顶点,以及附加的超级源s和超级汇t ;
2 对于图G 中每一条边(u ,v ),在N 中连边(u ,v ),容量为inf,费用为该边的长度;
3 从源点s 向所有d (v) <0的顶点v 连边(s,v),容量为-d (v),费用为0;
4 从所有d (v) > 0的顶点u 向汇点t 连边(u ,t),容量为d (v ),费用为0。

混合图CPP:

如果部分街道能够双向通行,部分街道只能单向通行。这个问题已被证明是NPC的。


原创粉丝点击