POJ 3164 Command Network(最小树形图模板题+详解)

http://poj.org/problem?id=3164

题意:

求最小树形图。

思路:

套模板。

引用一下来自大神博客的讲解:http://www.cnblogs.com/acjiumeng/p/7136604.html

算法步骤如下:

1.判断图的连通性,若不连通直接无解,否则一定有解。

2.为除了根节点以外的所有点选择一个权值最小的入边,假设用pre数组记录前驱,f数组记录选择的边长,记所选边权和为temp。

3.(可利用并查集)判断选择的的边是否构成环,若没有则直接$ans+=temp$并输出ans,若有,则进行下一步操作。

4.对该环实施缩点操作,设该环上有点$V1,V2……Vi……Vn$,缩成的点为node ,对于所有不在环中的点P进行如下更改:

(1) 点P到node的距离为min{$a[p,Vi]-f[Vi]$} (a为边集数组)

 (2)点node到p的距离为min{$a[Vi,p]$}

操作(1)的理解:先假设环上所有边均选上,若下次选择某一条边进入该环,则可以断开进入点与进入点的前驱之间的边,即断开F[进入点],所以等效为直接把$a[p,node]$赋值为min{$a[p,Vi]-f[Vi]$}。

特别提醒:本题有自环,可以提前删掉,因为它没有用。

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<sstream>
  6 #include<vector>
  7 #include<stack>
  8 #include<queue>
  9 #include<cmath>
 10 #include<map>
 11 #include<set>
 12 using namespace std;
 13 typedef long long ll;
 14 typedef pair<int,ll> pll;
 15 const int inf = 0x3f3f3f3f;
 16 const int maxn=1000+5;
 17 const int mod=1e9+7;
 18 
 19 int n, m;
 20 
 21 struct point
 22 {
 23     double x,y;
 24 }p[maxn];
 25 
 26 struct node
 27 {
 28     int u,v;
 29     double w;
 30 }edge[maxn*maxn];
 31 
 32 int pre[maxn],id[maxn],use[maxn];
 33 double in[maxn];
 34 
 35 double dis(point a,point b)
 36 {
 37     return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
 38 }
 39 
 40 double mini_tree(int root,int n,int m)//分别是树根,节点数,边数,序号从1开始
 41 {
 42     double ans=0;
 43     int u;
 44     while(true)
 45     {
 46         for(int i=1;i<=n;i++)  in[i]=inf;
 47         for(int i=1;i<=m;i++)
 48         {
 49             int u=edge[i].u;
 50             int v=edge[i].v;
 51             if(edge[i].w<in[v]&&u!=v)
 52             {
 53                 in[v]=edge[i].w;
 54                 pre[v]=u;
 55             }
 56         }//找最小的入边
 57         for(int i=1;i<=n;i++)
 58         {
 59             if(i==root)continue;
 60             ans+=in[i];//把边权加起来
 61             if(in[i]==inf)//如果存在没有入弧的点则不存在最小树形图
 62                 return -1;
 63         }
 64         memset(id,-1,sizeof(id));
 65         memset(use,-1,sizeof(use));
 66         int cnt=0;
 67         for(int i=1;i<=n;i++)//枚举每个点,搜索找环
 68         {
 69             int v=i;
 70             while(v!=root&&use[v]!=i&&id[v]==-1)
 71             {
 72                 use[v]=i;
 73                 v=pre[v];
 74             }
 75             if(v!=root&&id[v]==-1)//当找到环的时候缩点编号
 76             {
 77                 ++cnt;
 78                 id[v]=cnt;
 79                 for(u=pre[v];u!=v;u=pre[u])
 80                     id[u]=cnt;
 81             }
 82         }
 83         if(cnt==0)//如果没有环结束程序
 84             break;
 85         for(int i=1;i<=n;i++)//把余下的不在环里的点编号
 86             if(id[i]==-1)
 87                 id[i]=++cnt;
 88         for(int i=1;i<=m;i++)//建立新的图
 89         {
 90             int u=edge[i].u;
 91             int v=edge[i].v;
 92             edge[i].u=id[u];
 93             edge[i].v=id[v];
 94             if(edge[i].u!=edge[i].v)
 95                 edge[i].w-=in[v];
 96         }
 97         n=cnt;//更新节点数和根节点的编号
 98         root=id[root];
 99     }
100     return ans;
101 }
102 
103 int main()
104 {
105     //freopen("in.txt","r",stdin);
106     while(~scanf("%d%d",&n,&m))
107     {
108         for(int i=1;i<=n;i++)  scanf("%lf%lf",&p[i].x,&p[i].y);
109         for(int i=1;i<=m;i++)
110         {
111             scanf("%d%d",&edge[i].u,&edge[i].v);
112             if(edge[i].u!=edge[i].v)
113                 edge[i].w=dis(p[edge[i].u],p[edge[i].v]);
114             else edge[i].w=inf;
115         }
116         double ans=mini_tree(1,n,m);
117         if(ans==-1)  printf("poor snoopy
");
118         else printf("%.2f
",ans);
119     }
120     return 0;
121 }
原文地址:https://www.cnblogs.com/zyb993963526/p/7390646.html