[置顶] 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方法针对边,所以二者的数据结构有点不一样。

你可能感兴趣的:(问题)