您的位置:首页 > 其它

贪心算法思想分析及应用

2015-05-09 11:28 211 查看
贪心算法思想的核心在于量度的考量。基本步骤为:根据题意,选择一种量度。然后按照这个量度,对数据进行排序。依次输入一个数据,如果这个输入和当前已构成在这种量度意义下的部分最优解加在一起不能产生一个可行解,则不把此输入加到这部分解中。这种能够得到某种意义下的最优解的分级处理方法称为贪心算法。

背包算法,最小代价生成树,单源点最短路径都是应用了这种思想。

背包算法所选择的量度为装入物品单位容量获得当前最大的单位效益,即p[i]/w[i].根据这种量度,对物品的重量和所对应的效应进行排序。然后按照顺序装入包中,直至包装满。这里之所以选择这种量度,是因为为使装入的总效应值最大,这种顺序装入所得的总效应值最大。具体算法设计如下:

Void GreedyKnapsack( float m, float n){
//p[1: n]和w[1: n]分别含有按p[i]/w[i]≥p[i+1]/w[i+1]按序的n件物品的效益值和重量。m是背包的容量大小,而x(1: n)是解向量。//
for  ( int i =1 , i <= n; i++ ) x[i] = 0.0;//首先进行初始化,解为0
float U = m;
for  ( i =1 , i <= n; i++ ) {//此时的w[i]已经排好序,按照判断条件进行装入。
if ( w(i)>U ) break;
x[i] = 1.0;
U -= w[i];
}
if ( I <= n ) x[i] = U/w[i] ;
}


背包算法按照单位效应量排序,可以采用多种排序算法,如快速排序等。

最小代价生成树分析:

最小生成树也是通过贪心算法的思想来实现,它所选择的量度是选择一条边,使得目前包含的代价总和有最小程度的提高。即选择权值最小的边。它所运用的基本原理是1.先选入一个顶点为树的根节点,然后使未接入的顶点和它相连后,记录每个点和它相连的权值大小。

2.比较选出其中权值最小的和它相连的顶点,此顶点计入为下一个树节点。

3.这时,生成树中已生成2个树节点,再将未接入的图顶点与新接入的树节点的权值,和之前的相连树节点的权值进行比较。若与新接入的树节点之前的权值更小,则将其的邻节点改为这个新树节点。

4.算法中很重要的一个数组,就是close[]数组,它表示的是每个节点与图中已为树节点的最小的权值所构成的边

5.算法所采用的量度就是选取每阶段权值最小的边。

具体算法设计如下:

#include<stdio.h>
#include<time.h>
#include<stdlib.h>
//#include<limits.h>//该文件包含一些很有用的常量,它们定义了各种类型所能容纳的值
#include<FLOAT.H>

float prim(int n,float cost[][4],int t[][2]){

float mincost=0;
int close[4];
int k;
close[0]=-1;
for(int i=1;i<n;i++)
close[i]=0;

for(int j=0;j<n-1;j++)
{
float min=FLT_MAX;
for(int m=1;m<n;m++){
if(close[m]!=-1 && cost[m][close[m]]!=0 &&cost[m][close[m]]<min){//比较的是已接入的树节点与未接入的图顶点之间的权值大小的比较,选择权值最小的边接入树节点
min=cost[m][close[m]];
k=m;
}
}

t[j][0]=k;t[j][1]=close[k];
mincost=mincost+cost[k][close[k]];
close[k]=-1;

for(int s=1;s<n;s++){
if(close[s]!=-1 && cost[s][close[s]]!=0 && cost[s][k]!=0 && cost[s][close[s]] > cost[s][k])//比较的是已经接入的点和未接入点之间的权值大小和新接入的点与为接入的点的权值大小的比较
close[s]=k;
if(close[s]!=-1 && cost[s][close[s]]==0)
close[s]=k;
}

}

for(int x=0;x<n-1;x++)
printf("%d,%d\n",t[x][0],t[x][1]);

return mincost;

}

void main(){
/*

邻接矩阵是表示顶点之间相邻顶点之间相邻关系的矩阵
。设G=(V,E)是具有n个顶点的图,若(vi,vj)属于E,
则对应G的邻接矩阵中的元素A[i][j] = wij 或1,否则,A[i][j] = 0或无穷大,
其中,wij可以指边的权重。

**/
int n=4;
float cost[4][4]={
{0,6,1,5},
{6,0,5,0},
{1,5,0,5},
{5,0,5,0}

};
int t[4][2];
int k=1000; float mincost=0;
time_t t1,t2;

time(&t1);
for(int i=0;i<k;i++){
mincost=prim(n, cost, t);

printf("the mincost is %f\n",mincost);

}
time(&t2);
float number=(float)(t2-t1)/k;
printf("所花费的时间为%f\n",number);
}
单源点最短路径算法分析:

单源点最短路径,主要应用于带权有向图。其算法和最小生成树类似。所选用的量度是源点到其他各个点的最短路径。因为所应用的是图,故不能直接排序。只能从源点开始,对每个顶点的路径进行排序计入。具体算法如下:

#include<stdio.h>
#include<time.h>
#include<FLOAT.H>

void shortestPath(int v,float cost[][5],float dist[],int n,int close[])//V代表是源点,cost数组时权值,dist数组是其余每个顶点到源点的路径长度,close数组是每个顶点的最短路径长度的前一个节点。
{
bool s[5];
int u;
if(v<0||v>n-1) return;
//初始化,先将所有的点直接设置为何源点的最近距离就是他们直接相连的权值。不通或方向相反的距离都设置为无穷大。
for(int i=0;i<n;i++){
dist[i]=cost[v][i];

s[i]=false;
close[i]=v;

}
dist[v]=0;
s[v]=true;
close[v]=-1;

for(int j=0;j<n-1;j++){
float min=FLT_MAX;
for(int m=0;m<n;m++){
if(!s[m] && dist[m]<min){
min=dist[m];
u=m;
}
}
s[u]=true;
<pre name="code" class="html">//上面的这一个循环是比较初始化后,哪个顶点到源点的路径最短。将比较出的最短路径计入解中。这便是根据量度标准来做的。
for(int k=0;k<n;k++){


/*
写这个循环的目的在于,因为解中新加入一个顶点,这样源点与各个顶点的路径长度之和会有所改变。可能通过这个新加入的点,有了中间的过度,比直接的距离要更短。
**/
if(!s[k] &&<span style="color:#ff0000;"> cost[u][k]</span><FLT_MAX){
float newdist=<span style="color:#6633ff;"><strong>dist[u]+cost[u][k]</strong></span>;
<span style="color:#ff0000;">if(newdist<dist[k])</span>{
dist[k]=newdist;
close[k]=u;
}
}
}

}
for(int x=0;x<n;x++){
printf("%d到%d顶点的最短路径长度为:%f\n它的前一个经过的顶点为:%d\n",v,x,dist[x],close[x]);

}
}

void main(){
int k=1000;
time_t t1,t2;
int v=0;
int n=5;
float cost[5][5]={
{0,10,FLT_MAX,30,100},
{FLT_MAX,0,50,FLT_MAX,FLT_MAX},
{FLT_MAX,FLT_MAX,0,FLT_MAX,10},
{FLT_MAX,FLT_MAX,20,0,60},
{FLT_MAX,FLT_MAX,FLT_MAX,FLT_MAX,0}
};
float dist[5];
int close[5];
printf("你想求得哪个源点到其他顶点的最短路径?0-%d\n",n-1);
scanf("%d",&v);

printf("v=%d\n",v);
time(&t1);
for(int i=0;i<k;i++){
shortestPath(v,cost,dist,n,close);}

time(&t2);
float number=(float)(t2-t1)/k;
printf("所花费的时间为:%f\n",number);

}


由上面的算法可以看出,关于图的2个算法的设计,都是一个大的循环里嵌套2个小的循环。大循环执行的次数是n-1次,是图的边的总数。时间复杂度为O(n*n).

第一个小循环是按照量度标准来进行解的计入,下一个是由于新解计入,进行的再改变未计入的数据的属性。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: