• [置顶] NYOJ38 布线问题


    题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=38

    题目分析:
    其实就是求图的最小生成树。有两种方法。prim法和kruskal法。prim法只与节点有关,与边无关,所以适合于求边稠密的网的最小生成树。而kruskal算法与边有关,故其适合于求边比较稀疏的网络。
    prim算法:
    1)初始化set集为随意的一个节点,这里初始化为1。
    2)找出与set集中的点相连的,花费最小的并且不再set集中的点,加入set集中。为了计算的方便,先将每个节点相连的所有边按权值升序排列。
    3)直到所有的点都加到set集中,算法就停止了。
    kruskal算法:
    1)每次找权值最小的边,如果节点没有访问过,就加到set集中。如果访问过了,就合并两个set集。
    2)这里为了剪去不必要的迭代,如果连通区域为1,并且所有的点都已经访问过了,就退出。

    参考代码:

    prim算法的代码:

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    struct NODE
    {
    	int to;
    	int w;
    };
    
    NODE Map[501][501];//Map[i][0].to存放节点i相邻的点的个数
    bool used[501];
    int set[501];
    
    int compare(const void *a, const void *b)
    {
    	NODE *p1 = (NODE *)a;
    	NODE *p2 = (NODE *)b;
    	return p1->w - p2->w;
    }
    
    void GetMap(int n)
    {
    	for(int i = 1; i <= n; ++i)
    		qsort(&Map[i][1], Map[i][0].to, sizeof(Map[i][0]), compare);
    }
    
    int Prim(int n)
    {
    	int num = 1;//set集合内点的个数
    	int i,j;
    	int ans = 0;
    	NODE temp;
    	set[0] = 1;
    	used[1] = true;	
    	while(num < n)
    	{
    		temp.to = -1;
    		temp.w = 101;
    		for(i = 0; i < num; ++i)
    		{
    			for(j = 1; j <= Map[set[i]][0].to; ++j)
    			{
    				if(!used[Map[set[i]][j].to])
    				{
    					if(Map[set[i]][j].w < temp.w)
    						temp = Map[set[i]][j];
    					break;
    				}
    			}//end for j
    		}//end for i
    		if(temp.to != -1)
    		{
    			ans += temp.w;
    			set[num++] = temp.to;
    			used[temp.to] = true;
    		}
    	}//end for while
    	return ans;
    }
    
    int main()
    {
    	int t,i;
    	int v,e;
    	int a,b,c;
    	int ans;
    	scanf("%d", &t);
    	while(t--)
    	{
    		memset(used,0,sizeof(used));
    		scanf("%d %d", &v, &e);
    		for(i = 0; i <= v; ++i)
    			Map[i][0].to = 0;
    
    		for(i = 0; i < e; ++i)
    		{
    			scanf("%d %d %d", &a, &b, &c);
    			++(Map[a][0].to);
    			++(Map[b][0].to);
    			Map[a][Map[a][0].to].to = b;
    			Map[a][Map[a][0].to].w = c;
    			Map[b][Map[b][0].to].to = a;
    			Map[b][Map[b][0].to].w = c;
    		}
    		scanf("%d", &b);
    		for(i = 1; i < v; ++i)
    		{
    			scanf("%d", &a);
    			b = b < a ? b : a;
    		}
    		GetMap(v);
    		ans = Prim(v);
    		ans += b;
    		printf("%d\n", ans);
    	}
    	return 0;
    }

    kruskal算法的代码:

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    struct EDGE
    {
    	int from;
    	int to;
    	int w;
    };
    
    EDGE edge[124760];//所有的边
    bool used[501];
    int set[501];
    
    int compare(const void *a, const void *b)
    {
    	EDGE *p1 = (EDGE *)a;
    	EDGE *p2 = (EDGE *)b;
    	return p1->w - p2->w;
    }
    
    int find(int k)
    {
    	int r = set[k];
    	while(r != set[r])
    		r = set[r];
    	return r;
    }
    
    void Merge(int r1, int r2)
    {
    	if(r1 < r2)
    		set[r2] = r1;
    	else
    		set[r1] = r2;
    }
    
    int Kruskal(int v, int e)
    {
    	int ans = 0;
    	int t, num;//t为连通区域的个数,num为已访问的节点的个数
    	int r1, r2;
    	t = num = 0;
    	while(num != v && t != 1)
    	{
    		for(int i = 0; i < e; ++i)
    		{
    			if(!used[edge[i].from])
    			{
    				++t;
    				++num;
    				used[edge[i].from] = true;
    			}
    			if(!used[edge[i].to])
    			{
    				++t;
    				++num;
    				used[edge[i].to] = true;
    			}
    
    			r1 = find(edge[i].from);
    			r2 = find(edge[i].to);
    			if(r1 != r2)
    			{
    				--t;
    				Merge(r1, r2);
    				ans += edge[i].w;
    			}
    		}//end for i
    	}//end while
    	return ans;
    }
    
    int main()
    {
    	int t,i;
    	int v,e;
    	int a,b,c;
    	int ans;
    	scanf("%d", &t);
    	while(t--)
    	{
    		memset(used,0,sizeof(used));
    		scanf("%d %d", &v, &e);
    
    		for(i = 1; i <= v; ++i)
    			set[i] = i;
    
    		for(i = 0; i < e; ++i)
    		{
    			scanf("%d %d %d", &a, &b, &c);
    			edge[i].from = a;
    			edge[i].to = b;
    			edge[i].w = c;		
    		}
    		qsort(edge, e, sizeof(edge[0]), compare);
    		scanf("%d", &b);
    		for(i = 1; i < v; ++i)
    		{
    			scanf("%d", &a);
    			b = b < a ? b : a;
    		}
    		ans = Kruskal(v, e);
    		ans += b;
    		printf("%d\n", ans);
    	}
    	return 0;
    }
    

    由于prim方法针对节点,而kruskal方法针对边,所以二者的数据结构有点不一样。

  • 相关阅读:
    一些关于微电子方面的笔试题
    [JavaScript]Redeclaring variable
    [JavaScript]How to run Jasmine in Intellij/WebStorm?
    [JavaScript] Use `+` opertor to add one string and one number
    [CoffeeScript]CoffeeScript学习笔记
    Study Note for grunt
    grunt.js
    [转]Vim常用命令速查
    如何在Web Page里体验CoffeeScript的 功能
    [JavaScript] 如何使用Ruby gem运行jasmine test case
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/3027189.html
Copyright © 2020-2023  润新知