贪婪技术与Prim算法

时间:2014.06.06

地点:基地

---------------------------------------------------------------------------

一、简述贪婪技术

贪婪技术在贪婪过程中所做的每一步选择都满足如三个条件:

1.可行性:满足问题的约束

2局部优先:当前步骤中所有可行选择中最佳的局部选择

3不可取消:即选择一旦做出,在算法的后面步骤中无法改变

找零问题就是一个典型的贪婪技术应用:存在面额d1=25   d2=10  d3=5  d4=1的四种面额硬币,要求用最少硬币数给出找零sum=48的方案。答案是1个d1,2个d2,和3个d4 。在这里贪婪的每一步我们都是力求用满足问题约束的最大面额硬币,以使得余下的找零数额降到最低。对于该找零问题这个解也是最优解,

贪婪技术的核心是通过一系列步骤来构造问题的解,其中每一个步骤都对目前构造的部分解做一个扩展,直到获得问题的完整解为止。

贪婪地每一步,都要求贪婪地选择最佳操作,并希望通过一系列局部最优选择产生一个全局最优解。当然也并不是总是这样对于所有问题都凑效,局部最优并不意味着全局最优,但若关心的是近似解,用贪婪技术还是一样的靠谱。

---------------------------------------------------------------------------

二、Prim算法

Prim算法是用来求一个恶给定加权连通图的最小生成树的算法。连通图的生成树是一个包含图的所有顶点的连通无环子图,也就是一个树。加权连通图的最小生成树即图的一颗权重最小的生成树,树的权重即所有边的权重的总和。

Prim算法即是通过一系列不断扩张的子图来构造一颗最小生成树,算法首先从图的顶点集合V中任意选择一个顶点开始,作为一系列贪婪序列中的初始子树T0,每一次迭代都以一种贪婪的方式去扩张当前的生成树,把还不在树中的且离树最近的顶点加入到树中,当图中所有的顶点都包含在构造的树中以后,算法结束。算法每次迭代对树的扩张只添加一个顶点,于是迭代的总次数为n-1(共n个顶点,然后初始化了一个顶点)。在对树进行扩张时我们使用边的集合来表示算法的生成树。

算法伪代码:

Prim(G)
//输入:加权连通图G=<V,E>
//输出:E' ,组成G的最小生成树的边的集合
V' <——{ v0 }   //用任意顶点初始化树的顶点集合
E' <——空     // 此时边集还是空的
for i=[1....n-1]    //需n-1次迭代,n为顶点个数
    在所有的与树相连的边中(v,u)中求得权重最小的边 e*=(v*,u*)  //这里的与树相连的边意味着构成边的两个顶点一个在树中V',一个在树外V-V'
    V'<——V' U { u* }     //把新的顶点归入生成树中
    E'<——E' U { e* }    //把这条新的边归入生成树的边集中
return E'

---------------------------------------------------------------------------

三、Prim算法的实现技巧

为了找出与树相连的边中权重最小的边,我们需要让一个顶点提供两个附加的标记,该顶点连接到树中顶点的名称和对应权重,其中若是与树中任何顶点都不相连,用无穷大去标记。通过这些标记,问题求出加入当前树的下一个顶点的任务其实就是求出集合V-V‘中距离标记最小的顶点即可。

在确定加入树中的顶点u*后,有两个操作

一是 把u*从集合V-V‘中移动到顶点集合V‘中,即上面伪代码中的

<span style="font-size:18px;"> V'<——V' U { u* } </span>

二是 归入新顶点等操作后,对应集合V-V‘ 中剩下的顶点们u ,如果它们能用一条比当前自己和树的距离标记更短的边与刚刚新加入树中的顶点 u*相连,那么要把标记更新为 u* 和与 u*相连的边的权重。

---------------------------------------------------------------------------

五、Prim算法实现

#include<iostream>
#include<limits>
using namespace std;

struct Vertex
{
	int min_weight;    //存放当前离树最小的距离
	int parent_vertex; //存放当前离树最小距离已在树中的顶点
	bool is_in_tree;   //是否已在树中
};

size_t FindNearestVertes(Vertex vertex_set[], size_t size);
//Precondition:  Given a vertex set,finde the nearest vertes which is not in the Tree
//Postcondition: Return the nearest vertex's index
//Notice:        This is a auxiliary function
size_t FindNearestVertes(Vertex vertex_set[], size_t size);
//Precondition:
//Postcondition:
void Print(Vertex vertex_set[], int* graph[], size_t size);
//Precondition:
//Postcondition:

size_t FindNearestVertes(Vertex vertex_set[], size_t size)
{
	int min_weight = INT_MAX, nearest_vertex_index;
	for (size_t i = 0; i < size; ++i)
	{
		if (!(vertex_set[i].is_in_tree) && vertex_set[i].min_weight < min_weight)
		{
			min_weight = vertex_set[i].min_weight,
			nearest_vertex_index = i;
		}
	}
	return nearest_vertex_index;
}
Vertex* Prim(int* graph[], size_t size)
{
	Vertex* vertex_set = new Vertex[size];
	for (size_t i = 0; i < size; ++i)//初始化图的各顶点都离树很远也不再树中
	{
		vertex_set[i].min_weight = INT_MAX;
		vertex_set[i].is_in_tree = false;
	}
	vertex_set[0].min_weight = 0;  //初始化第一个顶点
	vertex_set[0].parent_vertex = -1;//

	int u;
	for (size_t i = 1; i < size; ++i)
	{
		u = FindNearestVertes(vertex_set, size);
		vertex_set[u].is_in_tree = true;
		for (size_t v = 0; v < size; ++v)
		{
			if (graph[u][v] && (!vertex_set[v].is_in_tree) && graph[u][v] < vertex_set[v].min_weight)
			{
				vertex_set[v].min_weight = graph[u][v];
				vertex_set[v].parent_vertex = u;
			}
		}
	}
	return vertex_set;
}
void Print(Vertex vertex_set[], int* graph[],size_t size)
{
	for (size_t i=1; i < size; ++i)
		cout << vertex_set[i].parent_vertex << "__" << i << ":   " << graph[i][vertex_set[i].parent_vertex] << endl;
}
int main()
{
	size_t N;
	cout << "Input vertex number: " << endl;
	cin >> N;
	cout << "Input a graph: " << endl;
	int** graph = new int*[N];
	{
		for (size_t i = 0; i < N; ++i)
		{
			graph[i] = new int[N];
			for (size_t j = 0;  j< N; ++j)
				cin >> graph[i][j];
		}
	}
	cout << "The result is: " << endl;
	Vertex* p=Prim(graph,N);
	Print(p, graph, 5);
}

贪婪技术与Prim算法

时间: 2024-07-31 15:51:27

贪婪技术与Prim算法的相关文章

最小生成树问题(prim算法)POJ-1258 Agri-Net

/* 这个题很水,但是,莫名其妙runtime error一晚上,重写了一遍就又没了,很伤心! 题意很简单,大致为n个村庄,连光缆,要求连上所有村庄的长度最短. 输入n,接着是n*n的矩阵,直接用prim算法写就行: */ #include<iostream> #include<cstdlib> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath>

杭电1162--Eddy&#39;s picture(Prim()算法)

Eddy's picture Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 8070    Accepted Submission(s): 4084 Problem Description Eddy begins to like painting pictures recently ,he is sure of himself to b

hihocoder1097最小生成树(prim算法)

prim算法描述: prim算法的思想和代码都跟dijkstra算法非常相似. 在dijkstra算法中,我们用每次取出未标记集合中到源点最近的点进行标记并更新其邻接点到源点的距离:当d[x]+w<d[y]时更新d[y]=d[x]+w. 而在prim算法中,我们只需要对dijkstra算法稍作改动即可,即只需要改变一下更新操作:当w<d[y]时更新d[y]=w,此时d[y]的含义是节点y连接到最小生成树的边的最小取值.也就是说,从图中某个节点发出了边可能有若干条,而最终将该节点连接到最小生成树

prim算法模板

var g:array[1..10,1..10] of longint; d:array[1..10] of longint; f:array[1..10] of boolean; procedure prim; var i,j,k,min:longint; begin fillchar(g,sizeof(g),0); fillchar(f,sizeof(f),0); for i:=1 to n do d[i]:=g[1,i]; f[1]:=true; for i:=2 to n do begi

最小生成树(prim算法,Kruskal算法)c++实现

1.生成树的概念 连通图G的一个子图如果是一棵包含G的所有顶点的树,则该子图称为G的生成树. 生成树是连通图的极小连通子图.所谓极小是指:若在树中任意增加一条边,则将出现一个回路:若去掉一条边,将会使之变成非连通图. 生成树各边的权值总和称为生成树的权.权最小的生成树称为最小生成树. 2.最小生成树的性质用哲学的观点来说,每个事物都有自己特有的性质,那么图的最小生成树也是不例外的.按照生成树的定义,n 个顶点的连通网络的生成树有 n 个顶点.n-1 条边. 3.构造最小生成树,要解决以下两个问题

Prim算法(一)之 C语言详解

本章介绍普里姆算法.和以往一样,本文会先对普里姆算法的理论论知识进行介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现. 目录 1. 普里姆算法介绍 2. 普里姆算法图解 3. 普里姆算法的代码说明 4. 普里姆算法的源码 转载请注明出处:http://www.cnblogs.com/skywang12345/ 更多内容:数据结构与算法系列 目录 普里姆算法介绍 普里姆(Prim)算法,和克鲁斯卡尔算法一样,是用来求加权连通图的最小生成树的算法. 基本思想 对于图G而言,V是所

HDU 5253 Prim算法

http://acm.hdu.edu.cn/showproblem.php?pid=5253 Prim算法是 1.每次选出 (已经选出的)点集 能够连接 边权值最小的点 2.使用新找出的点能带来的新的更小的边权,来更新旧的较大的边权 3.重复,直到连接所有点 的贪心算法 使用优先权队列优化 查找 边权值最小的点 的步骤. #include <stdio.h> #include <string.h> #include <stdlib.h> #include <ios

最小生成树--prim算法

一个无向图G的最小生成树就是由该图的那些连接G的所有顶点的边构成的树,且其总价值最低,因此,最小生成树存在的充分必要条件为图G是连通的,简单点说如下: 1.树的定义:有n个顶点和n-1条边,没有回路的称为树 生成树的定义:生成树就是包含全部顶点,n-1(n为顶点数)条边都在图里就是生成树 最小:指的是这些边加起来的权重之和最小 2.判定条件:向生成树中任加一条边都一定构成回路 充分必要条件:最小生成树存在那么图一定是连通的,反过来,图是连通的则最小生成树一定存在 上图的红色的边加上顶点就是原图的

[迷宫中的算法实践]迷宫生成算法&mdash;&mdash;Prim算法

       普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小.该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:Vojtěch Jarník)发现:并在1957年由美国计算机科学家罗伯特·普里姆(英语:Robert C. Prim)独立发现:1959年,艾兹格·迪科斯彻再次发现了该算法.因此,在某些场合,普里姆