• hdu 1233 还是畅通工程 最小生成树(prim算法 + kruskal算法)


    还是畅通工程

                                                                               Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)

    Problem Description
    某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。
     
    Input
    测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。
    当N为0时,输入结束,该用例不被处理。
     
    Output
    对每个测试用例,在1行里输出最小的公路总长度。
     
    Sample Input
    3 1 2 1 1 3 2 2 3 4 4 1 2 1 1 3 4 1 4 1 2 3 3 2 4 2 3 4 5 0
     
    Sample Output
    3 5
    Hint
    Hint
    Huge input, scanf is recommended.
     

     在无向带权连通图G中,如果一个连通子树包含所有顶点,并且连接这些顶点的边权之和最小,

    那么这个连通子图就是G的最小生成树。求最小生成树的一个常见算法是Prim算法。


    prim算法(时间复杂度为O(n^3)):

    Prim算法的基本思想是:

    1)设置两个集合V和S,任意选择一个顶点作为起始顶点,将起始顶点放入集合S,其余顶点存入集合

    V中;2)然后使用贪心策略,选择一条长度最短并且端点分别在S和V中边(即为最小生成树的中的一条

    边),将这条边在V中的端点加入到集合S中;3)循环执行第2)步直到S中包含了所有顶点。


    #include<stdio.h>
    #include<string.h>
    #define inf 0x3f3f3f3f
    int map[100][100],s[100],vis[100];
    int n,m;
    int prim()
    {
        int i,j,t,p,min,minpos,cnt;
        int ans=0;
        cnt=0;  /*记录已经加入的点的个数*/
        vis[1]=1; /*从第一个点开始找*/
        s[cnt++]=1; /*s数组保存已经加入的点*/
        while(cnt<n)  /*点还没有加入完*/
        {
            t=cnt;
            min=inf;
            for(i=0;i<t;i++) 
            {
                p=s[i];
                for(j=1;j<=n;j++)
                {
                    if(!vis[j]&&map[p][j]<min)  /*在已经加入的点和没加入的点之间找出一条最短路,*/
                    {
                        min=map[p][j];
                        minpos=j; /*记录下新找到的最短路的端点*/
                    }
                }
            }
            ans+=min;  
            s[cnt++]=minpos; /*更新已经加入的点*/
            vis[minpos]=1;  
        }
        return ans;
    }
    int main()
    {
        int u,v,w,i;
        while(~scanf("%d",&n)&&n)
        {
            m=n*(n-1)/2;
            memset(vis,0,sizeof(vis));
            memset(map,inf,sizeof(map));
            for(i=0;i<m;i++)
            {
                scanf("%d%d%d",&u,&v,&w);
                map[u][v]=w;
                map[v][u]=w;
            }
            int sum=prim();
            printf("%d
    ",sum);
        }
        return 0;    
    }

    上面的算法有三个循环,时间复杂度为O(N^3),考虑到由于使用的是贪心策略,则每添加一个新顶点到集合S中的时候,才会改变V中每个点到S中的点的最小边的长度。因此可以用一个数组nearest[N](N为顶点个数)记录在生成最小数的过程中,记录V中每个点的到S中点的最小边长,用另外一个数组adj[N]记录使得该边最小的对应的邻接点。那么O(N)的时间了找到最短的边,并且能在O(N)的时间里更新nearest[N]和adj[N]。因此可以得到O(N^2)的算法。

     

    #include<stdio.h>
    #include<string.h>
    #define inf 0x3f3f3f3f
    int map[100][100];
    int n,m;
    /*记当前生成树的节点集合为S,未使用的节点结合为V*/
    int vis[100]; //标记某个点是否在S中
    int adj[100]; //记录与S中的点最接近的点
    int nearest[100];  //记录V中每个点到S中的点的最短边
    int prim()
    {
    	int i,j,min;
    	int ans=0;
    	vis[1]=1;
    	for(i=2;i<=n;i++)
    	{
    		nearest[i]=map[1][i]; 
    		adj[i]=1;
    	}
    	int cnt=n-1; /*记录边的条数*/
    	while(cnt--)
    	{
    		min=inf;
    		j=1;
    		for(i=1;i<=n;i++)
    		{
    			if(!vis[i]&&nearest[i]<min)
    			{
    				min=nearest[i];
    				j=i;
    			}
    		}
    		ans+=map[j][adj[j]];
    		vis[j]=1;
    		for(i=1;i<=n;i++)
    		{
    			if(!vis[i]&&map[i][j]<nearest[i])
    			{
    				nearest[i]=map[i][j]; /*找最短的边*/
    				adj[i]=j; /*找最接近的点*/
    			}
    		}
    	}
    	return ans;
    }
    int main()
    {
    	int i,sum,u,v,w;
    	while(~scanf("%d",&n)&&n)
    	{
    		memset(vis,0,sizeof(vis));
    		memset(map,0,sizeof(map));
    		m=n*(n-1)/2;
    		for(i=0;i<m;i++)
    		{
    			scanf("%d%d%d",&u,&v,&w);
    			map[u][v]=map[v][u]=w;
    		}
    		sum=prim();
    		printf("%d
    ",sum);
    	}
    	return 0 ;
    }





    Kruskal算法(时间复杂度O(ElogE),E为边数):

    给定无向连同带权图G = (V,E),V = {1,2,...,n}。Kruskal算法构造G的最小生成树的基本思想是:

    (1)首先将G的n个顶点看成n个孤立的连通分支。将所有的边按权从小大排序。

    (2)从第一条边开始,依边权递增的顺序检查每一条边。并按照下述方法连接两个不同的连通分支:当查看到第k条边(v,w)时,如果端点v和w分别是当前两个不同的连通分支T1和T2的端点是,就用边(v,w)将T1和T2连接成一个连通分支,然后继续查看第k+1条边;如果端点v和w在当前的同一个连通分支中,就直接再查看k+1条边。这个过程一个进行到只剩下一个连通分支时为止。

    此时,已构成G的一棵最小生成树。


    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    int father[100];
    int n,m;
    struct point
    {
        int u;
        int v;
        int w;
    }a[5000];
    bool comp(point a1,point a2) /*按权值从小到大排序*/
    {
        return a1.w<a2.w;
    }
    void initial() /*并查集初始化*/
    {
        for(int i=0;i<=100;i++)
            father[i]=i;
    }
    int find(int x)  /*查找根节点*/
    {
        if(father[x]==x)
            return x;
        return find(father[x]);
    }
    void merge(int p,int q)  /*合并两个集合*/
    {
        int pp=find(p);
        int qq=find(q);
        if(pp!=qq)
        {
            if(pp<qq)
                father[qq]=pp;
            else
                father[pp]=qq;
        }
    }
    int kruskal()
    {
        initial();  /*初始化*/
        int ans=0;
        sort(a+1,a+m+1,comp); /*排序*/
        for(int i=1;i<=m;i++)
        {
            int x=find(a[i].u);
            int y=find(a[i].v);
            if(x!=y)  /*两端点不属于同一集合*/
            {
                ans+=a[i].w;
                merge(x,y); /*合并*/
            }
        }
        return ans;
    }
    int main()
    {
        int i,sum;
        while(~scanf("%d",&n)&&n!=0)
        {
            m=n*(n-1)/2;
            for(i=1;i<=m;i++)
                scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
            sum=kruskal();
            printf("%d
    ",sum);
        }
        return 0;
    }



  • 相关阅读:
    Using a custom AxisRenderer object
    进度条
    flex 自定义tooltip
    深入理解JAVA虚拟机 垃圾收集器和内存分配策略
    深入理解JAVA虚拟机 自动内存管理机制
    oracle pl/sql 程序设计 历史笔记整理
    oracle sql 高级编程 历史笔记整理
    JAVA并发编程的艺术 Java并发容器和框架
    JAVA并发编程的艺术 JMM内存模型
    Java并发编程实战 第16章 Java内存模型
  • 原文地址:https://www.cnblogs.com/dyllove98/p/3238846.html
Copyright © 2020-2023  润新知