最小树形图

最小树形图

何谓最小树形图,最小树形图,在一个有向图中,定义某个节点为根节点,由该根节点扩展出来的一颗有向的生成树,并且使这棵树的权值最小。练习题 POJ3164,

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<math.h>
 4 #define MAXN 205
 5 #define dis(x1,y1,x2,y2) (sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)))
 6 #define eps 1e-5
 7 typedef double elem_t;
 8 #define inf 1e10
 9 double x[MAXN], y[MAXN];
10 double map[MAXN][MAXN];
11 int pre[MAXN];
12 int c[MAXN][MAXN], l[MAXN], p[MAXN];
13 elem_t edmonds(int n,elem_t mat[][MAXN],int* pre)
14 {
15     elem_t ret=0;
16     int m=n,t,i,j,k;
17     for (i=0;i<n;l[i]=i,i++);
18     do{
19         memset(c,0,sizeof(c));
20         memset(p,0xff,sizeof(p));
21         
22         for (t=m,i=0;i<m;c[i][i]=1,i++);
23         
24         for (i=0;i<t;i++)
25             if (l[i]==i&&pre[i]!=-1){
26                 for (j=0;j<m;j++)
27                     if (l[j]==j&&i!=j&&mat[j][i]+eps<inf&&(p[i]==-1||mat[j][i]<mat[p[i]][i]))
28                         p[i]=j;
29                 if ((pre[i]=p[i])==-1)
30                     return -1;
31                 if (c[i][p[i]]){
32                     for (j=0;j<=m;mat[j][m]=mat[m][j]=inf,j++);
33                     for (k=i;l[k]!=m;l[k]=m,k=p[k])
34                         for(j=0;j<m;j++)
35                             if(l[j]==j){
36                                 if(mat[j][k]-mat[p[k]][k]<mat[j][m])
37                                     mat[j][m]=mat[j][k]-mat[p[k]][k];
38                                 if(mat[k][j]<mat[m][j])
39                                     mat[m][j]=mat[k][j];
40                             }
41                     c[m][m]=1,l[m]=m,m++;
42                 }
43                 for (j=0;j<m;j++)
44                     if (c[i][j])
45                         for (k=p[i];k!=-1&&l[k]==k;c[k][j]=1,k=p[k]);
46             }
47     }
48     while (t<m);
49     for (;m-->n;pre[k]=pre[m])
50         for (i=0;i<m;i++)
51             if (l[i]==m){
52                 for (j=0;j<m;j++)
53                     if (pre[j]==m&&mat[i][j]==mat[m][j])
54                         pre[j]=i;
55                 if (mat[pre[m]][m]==mat[pre[m]][i]-mat[pre[i]][i])
56                     k=i;
57             }
58     for (i=0;i<n;i++)
59         if (pre[i]!=-1)
60             ret+=mat[pre[i]][i];
61     return ret;
62 }
63 
64 int main(){
65     int n, m;
66     int s, t;
67     while(scanf("%d%d",&n,&m)!=EOF){
68         for(int i = 0; i < n; i++){
69             scanf("%lf%lf",x+i,y+i);
70         }
71         for(int i = 0; i < MAXN; i++)
72             for(int j = 0; j < MAXN; j++)
73                 map[i][j] = inf;    
74         for(int i = 0; i < n; i++)
75             pre[i] = 0;
76         
77         for(int i = 0; i < m; i++){
78             scanf("%d%d",&s,&t);
79             s--, t--;
80             map[s][t] = dis(x[s],y[s],x[t],y[t]);
81         }
82         pre[0]=-1;
83         double ans = edmonds(n,map,pre);
84         if(ans < 0)    
85             printf("poor snoopy\n");
86         else
87             printf("%.2lf\n", ans);
88     }
89     return 0;
90 }
91 
92 
解决该问题的算法有两位中国人提出,有位同学将该论文总结一下:
最小树形图算法(Zhu-Liu Algorithm):

1.  设最小树形图的总权值为cost,置cost为0。

2.  除源点外,为其他所有节点Vi找一条权值最小的入边,加入集合T。T就是最短边的集合。加边的方法:遍历所有点到Vi的边中权值最小的加入集合T,记pre[Vi]为该边的起点,mincost[Vi]为该边的权值。

3.  检查集合T中的边是否存在有向环,有则转到步骤4,无则转到步骤5。这里需要利用pre数组,枚举检查过的点作为搜索的起点,类似dfs的操作判断有向环。

4.  将有向环缩成一个点。设环中有点{Vk1,Vk2,…,Vki}共i个点,用Vk代替缩成的点。在压缩后的图中,更新所有不在环中的点V到Vk的距离:

map[V][Vk] = min {map[V][Vkj]-mincost[Vki]} 1<=j<=i;

map[Vk][V] = min {map[Vkj][V]}           1<=j<=I

5.  cost加上T中有向边的权值总和就是最小树形图的权值总和。

你可能感兴趣的:(最小树形图)