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\n"); 118 else printf("%.2f\n",ans); 119 } 120 return 0; 121 }
时间: 2024-10-11 23:22:34