POJ 1278 Agri-Net 最小生成树模板题(Prim 与 Kruskal)

这是我的第一篇oj记录博文,请大家以后多多指教。今天本来想做poj 3241曼哈顿最小生成树的那道题,结果学了半天发现自己连Kruskal都不能正确实现,所以就回来重刷了一下最小数模板题的此题,并之前的prim算法的解法的代码一起发出。

Prim与Kruskal算法都是求解最小生成树的有效方法,但是两者的高效实现都依赖于巧妙的数据结构。Kruskal算法如果借助于并查集,则能达到O(E*alpha(E))的求解时间(其中alpha函数是一个增长及其缓慢的函数,在这里可以视为是常数),另外加上排序的时间,总时间为O(E log(E) ); 而prim算法中要维护一个最小优先队列,而且要求能在O(1)时间内查找元素并改变它的值,我暂时没有了解到很好的方法,而二叉堆的查找元素时间还是O(n)的,所以还不如直接用数组维护。

poj 1278题面在此不再赘述,就是一道顶点数<100的最小生成树的题目。以下附上个人代码。编程习惯用camelVariant命名法,另外变量名较长,请多担待。

Prim方法:

#include <stdio.h>
#include <limits.h>
#define Size 101

typedef int iter;
int farms[Size][Size];
struct Vertex {
	int key;
	int piNumber;
	bool isValid;
};
typedef struct Vertex Vertex;
Vertex vertices[Size];
int extractMin(int size) 
{
	int isNull = true;
	int tempMin = INT_MAX;
	int tempMinIndex = -1;
	for (int i = 0; i < size; i++) {
		if (!vertices[i].isValid)
			continue;
		else {
//			printf("Here.\n");
			isNull = false;
			if (tempMin > vertices[i].key) {
				tempMinIndex = i;
				tempMin = vertices[i].key;
			}
		}
	}
//	printf("TempMinIndex:%d\n", tempMinIndex);
	if (!isNull) {
		vertices[tempMinIndex].isValid=false;
	}
	return tempMinIndex;
}
bool isBeLong(int size,int checkNumber)
{
	return vertices[checkNumber].isValid && size > checkNumber;
}
int main()
{
	iter i, j, k;
	int size,temp;
	while (scanf("%d", &size) != EOF) {
		for (i = 0; i < size; i++)
			for (j = 0; j < size; j++)
				scanf("%d", &farms[i][j]);
/*		for (i = 0; i < size; i++) {
			for (j = 0; j < size; j++) {
				printf("%d", farms[i][j]);
				j == size - 1 ? printf("\n") : printf("\t");
			}
		}*/
		
		for (i = 0; i < size; i++) {
			vertices[i].piNumber = -1;
			vertices[i].key =1000000;
			vertices[i].isValid = true;
		}
		temp = extractMin(size);
		while (temp >= 0) {
//			printf("Temp:%d\n", temp);
			for (i = 0; i < size; i++) {
				if (i != temp) {
					if (farms[temp][i] < vertices[i].key&&isBeLong(size, i)) {
						vertices[i].piNumber = temp;
						vertices[i].key = farms[temp][i];
					}
				}
			}
			temp = extractMin(size);
		}
		int value = 0;
		for (int i = 0; i < size; i++)
			value += farms[i][vertices[i].piNumber];
		printf("%d\n", value);
	}
	return 0;
}


Kruskal方法:


#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <vector>
#include <algorithm>
using namespace std;

struct Edge {
	int from;
	int to;
	int weight;
	bool operator < (const Edge& ele) {
		return this->weight < ele.weight;
	}
};
typedef struct Edge Edge;

class KruskalGraph
{
public:
	void init(int n)
	{
		this->edges.clear();
		this->vertices.clear();
		for (int i = 0; i < n; i++)
			this->vertices.push_back(-1);
		this->treeEdges.clear();
		this->order = n;
	}
	void Inserto(int u, int v, int w)
	{
		Edge tempEdge;
		tempEdge.from = u;
		tempEdge.to = v;
		tempEdge.weight = w;
		this->edges.push_back(tempEdge);
	}
	void Kruskal()
	{
		sort(edges.begin(), edges.end());
		int count = 0;
		for (int i = 0; i < edges.size()&&count<order; i++) {
			int root1, root2;
			root1 = Find(edges[i].to);
			root2 = Find(edges[i].from);
			if (root1 != root2) {
				this->treeEdges.push_back(edges[i]);
				Union(root1, root2);
				count++;
			}
		}
	}
	void showMST()
	{
		for (int i = 0; i < treeEdges.size(); i++) {
			printf("(%d,%d,%d) ", treeEdges[i].from, treeEdges[i].to, treeEdges[i].weight);
		}
		printf("\n");
	}
	int mstValu()
	{
		int ret = 0;
		for (int i = 0; i < treeEdges.size(); i++)
			ret += treeEdges[i].weight;
		return ret;
	}
private:
	vector<Edge > edges;
	vector<Edge > treeEdges;
	vector<int > vertices;
	int order;
	void Union(int x, int y)
	{
		int root1, root2;
		root1 = Find(x);
		root2 = Find(y);
		//printf("Roots:%d %d\n", root1, root2);
		_union(root1, root2);
	}
	int Find(int x)
	{
		if (vertices[x] < 0)
			return x;
		else
			return vertices[x] = Find(vertices[x]);
	}
	void _union(int x, int y)
	{
		if (vertices[y] < vertices[x]) {
			vertices[x] = y;
		}
		else {
			if (vertices[x] == vertices[y])
				vertices[x]--;
			vertices[y] = x;
		}
	}
};
int main()
{
	int n;
	KruskalGraph graph;
	while (scanf("%d", &n) != EOF) {
		graph.init(n);
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				int weight;
				scanf("%d", &weight);
				if (i > j)
					graph.Inserto(i, j, weight);
			}
		}
		graph.Kruskal();
		//graph.showMST();
		printf("%d\n", graph.mstValu());
	}
	return 0;
}

另外附上今天学习到的两个模板:

一个并查集的:

class UnionFindSet : vector<int >
{
public:
	UnionFindSet(int k)
	{
		this->clear();
		for (int i = 0; i < k; i++) {
			this->push_back(-1);
		}
	}
	void show()
	{
		for (int i = 0; i < this->size(); i++) {
			printf("%d ", (*this)[i]);
		}
		printf("\n");
	}
	int find(int x)
	{
		if ((*this)[x] < 0)
			return x;
		else
			return (*this)[x] = find((*this)[x]);
	}
	void Union(int x, int y)
	{
		int root1, root2;
		root1 = find(x);
		root2 = find(y);
		printf("Roots:%d %d\n", root1, root2);
		_union(root1, root2);
	}
private:
	void _union(int x, int y)
	{
		if ((*this)[y] < (*this)[x]) {
			(*this)[x] = y;
		}
		else {
			if ((*this)[x] == (*this)[y])
				(*this)[x]--;
			(*this)[y] = x;
		}
	}
};

另外一个别人的Binary Indexed Tree的,先学着,看明天做曼哈顿树的那道题的时候用不用的上

class BinTree : vector<int >
{
public:
	explicit BinTree(int k = 0) {
		assign(k + 1, 0);
	}
	int sum(int k)
	{
		return k > 0 ? sum(k - lowBit(k)) + (*this)[k] : 0;
	}
	int last()
	{
		return size() - 1;
	}
	void add(int k, int w)
	{//for adding w to node k
		if (k > last())
			return;
		(*this)[k] += w;
		add(k + lowBit(k), w);
	}
	void show(int start, int end)
	{
		for (int i = start; i <= end; i++) {
			printf("%d ", (*this)[i]);
		}
		printf("\n");
	}
	void show()
	{
		for (int i = 0; i < this->size(); i++) {
			printf("%d ", (*this)[i]);
		}
		printf("\n");
	}
private:
	int lowBit(int k)
	{
		return k&-k;
	}
};

今天就写到这里。上述有些技术细节我以后写文过程中有空的时候会提及。以后还请多关照了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kruskal算法是一种用来求解最小生成树的贪心算法。它的基本思想是,按照边的权值从小到大的顺序选择边,并且保证所选的边不会形成环,直到选取了n-1条边为止。另外,Kruskal算法还需要使用并查集来判断两个节点是否属于同一个连通分量。 在具体的实现过程中,可以按照以下步骤进行: 1. 将图中的所有边按照权值从小到大排序。 2. 创建一个并查集,并初始化每个节点为一个独立的集合。 3. 遍历排序后的边列表,对于每一条边(u, v),判断u和v是否属于同一个连通分量。如果不属于,则将这条边加入最小生成树中,并将u和v合并到同一个连通分量中。 4. 重复步骤3,直到最小生成树中的边数达到n-1。 通过以上步骤,就可以使用Kruskal算法求解出给定图的最小生成树。 参考资料: 引用:(1)度限制最小生成树和第K最短路. (poj1639) (2)最短路,最小生成树,二分图,最大流问的相关理论(主要是模型建立和求解) (poj3155, poj2112,poj1966,poj3281,poj1087,poj2289,poj3216,poj2446 (3)最优比率生成树. (poj2728) (4)最小树形图(poj3164) (5)次小生成树. (6)无向图、有向图的最小环 。 引用:http://home.ustc.edu.cn/~zhuhcheng/ACM/segment_tree.pdf 。 引用:练习复杂一点,但也较常用的算法。 二分图匹配(匈牙利),最小路径覆盖 网络流,最小费用流。 线段树. 并查集。 熟悉动态规划的各个典型:LCS、最长递增子串、三角剖分、记忆化dp 6.博弈类算法。博弈树,二进制法等。 7.最大团,最大独立集。 8.判断点在多边形内。 差分约束系统. 双向广度搜索、A*算法,最小耗散优先. 第三阶段: 。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值