hdu1102 hdu1233 hdu4081 hdu4126 最小生成树

Hdu1102 最小生成树 Prime算法(裸):首先有两个集合V{},边的集合E{}。先选择一个点,加入到V集合中,然后选择和

这个V集合中的点相关连的边中最小的,将边的另一头的点加入到V集合中,该边加入到E集合中,V集合中的点是一个连通图,

每次找和这个连通图相连的最短边,来更新。

View Code
 1 #include <stdio.h>
 2 #include <string.h>
 3 const int N=105;
 4 const int inf=10000000;
 5 int map[N][N],n;
 6 int vis[N*(N+1)/2];
 7 int dis[N*(N+1)/2];
 8 void Prime(){
 9     int i,j,t;
10     memset(vis,0,sizeof(vis));
11     for(i=1;i<=n;i++){
12        if(i==1) dis[i]=0;
13        else dis[i]=map[1][i];
14     }
15     int loc,sum=0;
16     for(i=1;i<=n;i++){
17        int min=inf;
18        for(j=1;j<=n;j++){
19            if(!vis[j]&&dis[j]<min){
20                min=dis[j];
21                loc=j;
22            }
23        }
24        vis[loc]=1;
25        sum+=min;
26        for(j=1;j<=n;j++){
27            if(!vis[j]&&map[loc][j]<dis[j]){
28                dis[j]=map[loc][j];
29            }
30        }
31     }
32     printf("%d\n",sum);
33 }
34 int main(){
35     int m,a,b;
36     while(scanf("%d",&n)!=EOF){
37         for(int i=1;i<=n;i++){
38             for(int j=1;j<=n;j++){
39                 scanf("%d",&map[i][j]);
40             }
41         }
42         scanf("%d",&m);
43         for(int i=0;i<m;i++){
44             scanf("%d%d",&a,&b);
45             map[a][b]=map[b][a]=0;
46         }
47         Prime();
48     }
49     return 0;
50 }

Hdu1233利用Kruskal算法(裸):

Kruskal利用了并查集,将所有的边按照权值从小到大排序,第一条边是必选的,然后看第二条边,如果两个点在一个连通图中,

则该边不选,否则选,如此下去,每次检查一个边要或者不要就取决于两个点是否已经在连通了,连通了则该边不要,不连通则

该边要,并且把该边加到连通图上。

View Code
 1 #include <stdio.h>
 2 #include <iostream>
 3 using namespace std;
 4 #include <algorithm>
 5 const int N=105;
 6 int father[N];
 7 int find(int x){
 8     if(x!=father[x])
 9         father[x]=find(father[x]);
10     return father[x];
11 }
12 struct edge{
13     int x,y,v;
14 }e[N*(N-1)/2];
15 int cmp(edge e1,edge e2){
16     return e1.v<e2.v;
17 }
18 int main(){
19     int n;
20     while(scanf("%d",&n)!=EOF&&n){
21         for(int i=0;i<=n;i++){
22             father[i]=i;
23         }
24         n=n*(n-1)/2;
25         for(int i=0;i<n;i++){
26             scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].v);
27         }
28         sort(e,e+n,cmp);
29         int res=0;
30         for(int i=0;i<n;i++){
31             int x=find(e[i].x);
32             int y=find(e[i].y)
33             if(x!=y) {
34                 res+=e[i].v;
35                 father[x]=y;
36             }
37         }
38         printf("%d\n",res);
39     }
40     return 0;
41 }

HDU4081 最小生成树变形。

题意:

  有n坐城市,输入每坐城市的坐标和人口。

  现在要在所有城市之间修路,保证每个城市都能相连,并且保证A/B 最大,所有路径的花费和最小

  A是某条路i两端城市人口的和

  B表示除路i以外所有路的花费的和(路径i的花费为0)

方法:

  ①先求一棵最小生成树,然后枚举每一条最小生成树上的边,删掉后变成两个生成树,然后找两个集合中点权最大的两个

  连接起来。这两个点中必然有权值最大的那个点,所以直接从权值最大的点开始dfs。

  为了使A/B的值最大,则A尽可能大,B尽可能小。所以B中的边一定是MST上去掉一条边后的剩余所有边。首先用O(N^2)算出

  MST,然后依次枚举,删去MST上的每一条边,MST变成两棵树T1和T2,然后在剩余的边(即不在MST上的边),以及这条删

  去的边中找到该边的两点的权值和最大以及能够连接T1和T2的边,A=删去边后的替换边的两点的权值和,B=删去该边后的MST

  的值,求A/B最大。则A尽可能大,A分别是T1和T2中最大的两个点,则所有点中权值最大的点一定在A中,由此在MST上从权值

  最大的点作为root,开始dfs。递归求出子树中的每个最大的点以及求出A/B的比值,求出最大。

重点在于dfs:

View Code
 1 int dfs(int u){
 2 
 3     vis[u]=1;
 4 
 5     int temp;
 6 
 7     int t_max=pw[u];//pw是pointWeight每个点的权值
 8 
 9     for(int i=head[u];i!=-1;i=t_e[i].next){//从root开始在MST上dfs
10 
11         int v=t_e[i].v;
12 
13         if(!vis[v]){
14 
15             temp=dfs(v);
16 
17             t_max=max(t_max,temp);//每次找出子树中的权值最大点
18 
19             ans=max(ans,(temp+maxPoint)/(mst-t_e[i].w));//删除该边
20 
21         }
22 
23     }
24 
25     return t_max;
26 
27 }

  ②求一棵生成树。你可以使其中一条边权值变成0.使得该边两点的权值和除以生成树的边权和最大。

  首先求最小生成树,然后枚举每条边。

  如果这条边在MST上,那么直接把人口除以(MST - magic road) 即可

  但是,如果这条边不在MST上,假设两顶点分别是(u,v)

  那么这条边肯定比uàv的MST连边的边的权值都要大。

  因为如果要是小的话,那这条边就会加到MST中了。

  如果把这条边当作magic road的话,那么这条边以及连接(u,v)的MST的边就组成了一个环了

  当前这条边的权值是最大的,要使剩下的路的花费最小,那么肯定要把u v间的最长的一条边给删去就行了

  也就是找环中的第二大边了,那么,求任意两点间的最长先预处理的。

  求一棵最小生成树,通过类似DP的预处理将每两点之间的最大边求出来,然后,一个二重循环枚举每对点,减掉最大边,

  添上点权就行。(解题报告上这么写的,但是个人感觉不需要二重循环,只需要求出权值最大点到其余每个点的路径中最

  长的边,然后一重循环,这样又回到了第一种方法上)。

方法①的代码实现(方法②的还没写):

View Code
  1 #include <stdio.h>
  2 #include <math.h>
  3 #include <string.h>
  4 #include <iostream>
  5 #include <algorithm>
  6 using namespace std;
  7 #define N 1005
  8 int edgeNum;
  9 double mst,ans;
 10 int maxPoint,root;
 11 int father[N],pw[N],vis[N];
 12 int head[N],t_edgeNum;
 13 int find(int x){
 14     if(x!=father[x])
 15         father[x]=find(father[x]);
 16     return father[x];
 17 }
 18 struct point{
 19     int x,y;
 20 }p[N];
 21 struct edge{
 22     int u,v;
 23     double w;
 24 }e[N*(N-1)/2];
 25 struct t_edge{
 26     int v,next;
 27     double w;
 28 }t_e[2*N];
 29 void add(int u,int v,double w){
 30     t_e[t_edgeNum].v=v;
 31     t_e[t_edgeNum].w=w;
 32     t_e[t_edgeNum].next=head[u];
 33     head[u]=t_edgeNum++;
 34     t_e[t_edgeNum].v=u;
 35     t_e[t_edgeNum].w=w;
 36     t_e[t_edgeNum].next=head[v];
 37     head[v]=t_edgeNum++;
 38 }
 39 void init(){
 40     ans=0;
 41     edgeNum=0;
 42     t_edgeNum=0;
 43     mst=maxPoint=0;
 44     memset(vis,0,sizeof(vis));
 45     memset(head,-1,sizeof(head));
 46     for(int i=0;i<N;i++){
 47         father[i]=i;
 48     }
 49 }
 50 double getlen(point p1,point p2){
 51     return sqrt((double)((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)));
 52 }
 53 int cmp(edge e1,edge e2){
 54     return e1.w<e2.w;
 55 }
 56 int dfs(int u){
 57     vis[u]=1;
 58     int temp;
 59     int t_max=pw[u];
 60     for(int i=head[u];i!=-1;i=t_e[i].next){
 61         int v=t_e[i].v;
 62         if(!vis[v]){
 63             temp=dfs(v);
 64             t_max=max(t_max,temp);
 65             ans=max(ans,(temp+maxPoint)/(mst-t_e[i].w));
 66         }
 67     }
 68     return t_max;
 69 }
 70 int main(){
 71     int n,t;
 72     scanf("%d",&t);
 73     while(t--){
 74         init();
 75         scanf("%d",&n);
 76         for(int i=0;i<n;i++){
 77             scanf("%d%d%d",&p[i].x,&p[i].y,&pw[i]);
 78             if(pw[i]>maxPoint){
 79                 maxPoint=pw[i];
 80                 root=i;
 81             }
 82         }
 83         for(int i=0;i<n;i++)
 84             for(int j=i+1;j<n;j++){
 85                 e[edgeNum].u=i;
 86                 e[edgeNum].v=j;
 87                 e[edgeNum].w=getlen(p[i],p[j]);
 88                 edgeNum++;
 89             }
 90         sort(e,e+edgeNum,cmp);
 91 
 92         for(int i=0;i<edgeNum;i++){
 93             int fa=find(e[i].u);
 94             int fb=find(e[i].v);
 95             if(fa!=fb){
 96                 father[fa]=fb;
 97                 mst+=e[i].w;
 98                 //printf("(%d,%d) %.4lf\n",e[i].u,e[i].v,e[i].w);
 99                 add(e[i].u,e[i].v,e[i].w);
100             }
101         }
102         //printf("mst:%.4lf\n",mst);
103         dfs(root);
104         printf("%.2lf\n",ans);
105     }
106     return 0;
107 }

HDU4126最小生成树

题意:

  n个点,m个边,然后给出m条边的顶点和权值,其次是q次替换,每次替换一条边,给出每次替换的边的顶点和权值,

  求q次替换之后的平均值。其中n<3000,q<10000。

 思路:

  ①首先第一个想到的是暴力,求出Q次MST,显然是不可行的O(q*n^2);

  ②然后分析一下,利用Prime算法,其中q次替换中不在MST上的边不需要替换,所以时间复杂度降低到O(n^3),但是还是太暴力。

  ③如果替换的边在MST上,那么将MST上这条边去掉,假设修改的边是(a,b)并把(a,b)的权值修改为c,那个我们把(a,b)边从最小

  生成树中去掉,就形成两棵树T1,T2,那么最小生成树就可以由T1,T2加上连接 T1 和T2的权值最小的边。此时这个最小边就是最

  有替换边。我想sort一下不在MST上的所有的边,然后找到能连接T1和T2的最小边。TLE了……

  ④后来看了解题报告,树形dp,从每个点dfs一次,每次把i当成根,其余都是它的孩子,更新dp数组,对于i点为根的除j之外的所有的

  子树中的所有点到j距离最小值。每次从一个点pos开始dfs,搜索到最后一个叶子,开始看map[pos][u]的大小,保证(pos,u)

  不是MST上的的边,那么一路返回,连接叶子节点的那条边的最佳替换边就是map[pos][u]的大小,再继续返回,

  此过程要看,map[pos][...]的大小,其中[...]表示从叶子节点一路返回过来的点。

  测试数据:

  

8 14
0 1 4
0 2 3
1 2 5
1 3 5
1 4 9
2 3 5
2 7 5
3 4 7
3 5 6
3 6 5
3 7 4
4 5 3
5 6 2
6 7 6
1
0 1 5

View Code
 1 #include <vector>
 2 #include <stdio.h>
 3 #include <iostream>
 4 #include <algorithm>
 5 using namespace std;
 6 const int N=3000+5;
 7 const int inf=1000000000;
 8 struct edge{
 9     int u,v,w;
10 }e[N*N];//所有的边
11 int n,m,q;
12 int a,b,c;
13 int father[N];
14 int map[N][N];//map[i][j]表是(i,j)边权值
15 int dp[N][N];//dp[i][j]表示去掉MST上的(i,j)边后的最佳替换边的长度
16 bool vis[N][N];//标记是否在MST上
17 vector<int> Edge[N];
18 int min(int a,int b){return a<b?a:b;}
19 int find(int x){
20     if(x!=father[x])
21         father[x]=find(father[x]);
22     return father[x];
23 }
24 //用于Kruskal使用
25 int cmp(edge e1,edge e2){
26     return e1.w<e2.w;
27 }
28 //更新dp[i][j],对于i点为根的除j之外的所有子树中的所有的点到j距离的最小值
29 //确定这些点和j不在一个集合里
30 int dfs(int pos,int u,int fa){//求pos点到以u为根的树及其子树的最小距离
31     int ans=inf;
32     for(int i=0;i<Edge[u].size();i++){
33         int v=Edge[u][i];
34         if(v==fa) continue;
35         int tmp=dfs(pos,v,u);
36         ans=min(ans,tmp);
37         dp[u][v]=dp[v][u]=min(dp[u][v],tmp);
38         //通过dfs的返回值来更新dp[i][j]
39     }
40     if(pos!=fa) //保证这条边不是生成树的边,不然不能更新
41         ans=min(ans, map[pos][u]);
42     return ans;
43 }
44 int main(){
45     while(scanf("%d%d",&n,&m)!=EOF){
46         if(n==0&&m==0) break;
47         double mst=0,sum=0;
48         for(int i=0;i<n;i++){
49             Edge[i].clear();
50             father[i]=i;
51             for(int j=0;j<n;j++)
52                 map[i][j]=dp[i][j]=inf,
53                 vis[i][j]=1;
54         }
55         for(int i=0;i<m;i++){
56             scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
57             map[e[i].u][e[i].v]=map[e[i].v][e[i].u]=e[i].w;
58         }
59         sort(e,e+m,cmp);
60         for(int i=0;i<m;i++){
61             a=find(e[i].u);
62             b=find(e[i].v);
63             if(a!=b){
64                 father[a]=b;
65                 mst+=e[i].w;
66                 Edge[e[i].u].push_back(e[i].v),
67                 Edge[e[i].v].push_back(e[i].u);
68                 vis[e[i].u][e[i].v]=vis[e[i].v][e[i].u]=0;
69             }
70         }
71         for(int i=0;i<n;i++){
72             dfs(i,i,-1);
73         }
74         scanf("%d",&q);
75         for(int i=0;i<q;i++){
76             scanf("%d%d%d",&a,&b,&c);
77             if(vis[a][b]==1)
78                 sum+=mst;
79             else
80                 sum+=mst*1.0-map[a][b]+min(dp[a][b],c);
81         }
82         printf("%.4lf\n",sum/(double)q);
83     }
84     return 0;
85 }

 

 

 

posted @ 2013-01-11 00:18  _sunshine  阅读(2411)  评论(0编辑  收藏  举报