贪心算法总结

贪心算法

        贪心算法是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关 。简单来说就是每一步总是做出在当前看来最好的选择,也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。

解决贪心问题的思路

1.建立数学模型来描述问题;

2.把求解的问题分成若干个子问题;

3.对每一子问题求解,得到子问题的局部最优解;

4.把子问题的解局部最优解合成原来解问题的一个解。

贪心策略适用的前提是:局部最优策略能导致产生全局最优解。

实际上,贪心算法适用的情况很少。一般,对一个问题分析是否适用于贪心算法,可以先选择该问题下的几个实际数据进行分析,就可做出判断,因为用贪心算法只能通过解局部最优解的策略来达到全局最优解,因此,一定要注意判断问题是否适合采用贪心算法策略,找到的解是否一定是问题的最优解。贪心算法并不是完全不可以使用,贪心策略一旦经过证明成立后,它就是一种高效的算法。

 典型例题   

1、单源最短路径:迪杰斯特拉算法

算法流程总结:

 (1)初始化:顶点集S为空,从V0出发到图上其余各顶点Vi可能到达的最短路径长度初值:D[i]=arc[locate(G,V0)][i](Vi ϵ V), 也就是D[0]=0,其他为+∞。

 (2)选择节点Vj,加入集合S,U=V-S,使得:D[j]=Min{D[i]|Vi ϵ U}。其中Vj就是当前求的从v0出发的最短路径的终点

 (3)修改从V0出发到集合U上的任一顶点Vk可达的最短路径长度,若:D[j]+arc[j][k]<D[k],则使得D[k]=D[j]+arc[j][k]

 (4)重复(2)和(3),直到U为空。求的V0 到其余顶点的最短路径是依路径长度递增的序列。

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
struct node{
	int a[100][100]; 
	int v;//顶点数目 
}; 
struct node a;
int dist[100];//到源点的距离 
int flag[100];//标记是否已添加 
int pre[100];//指明当前点的路径的上一个点 
int max1=99999;
int zuixiao()
{
	int min1=99999;
	int minv;
	for(int i=0;i<a.v;i++)
	{
		if(flag[i]==0&&dist[i]<min1)
		{
			min1=dist[i];
			minv=i;
		}
	}
	if(min1<max1)
	return minv;
	else return -1;
}
bool dijkstra(int x)
{
	for(int i=0;i<a.v;i++)
	{
		flag[i]=0;
		dist[i]=a.a[x][i];
		if(dist[i]<max1)
		pre[i]=x;
		else pre[i]=-1;
	}
	flag[x]=1;
	dist[x]=0;
	while(1)
	{
		int y=zuixiao();
		if(y==-1)
		break;
		flag[y]=1;
		for(int i=0;i<a.v;i++)
		{
			if(flag[i]==0&&a.a[y][i]<max1)
			{
				if(a.a[y][i]<0)
				return false;
				if(dist[y]+a.a[y][i]<dist[i])
				{
					dist[i]=dist[y]+a.a[y][i];
					pre[i]=y;
				}
			}
		}
	}
	return true;
}
int main()
{
	cout<<"请输入点的个数"<<endl;
	int n;
	cin>>n;
	a.v=n;
	cout<<"请输入图的距离矩阵"<<endl;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n;j++)
		{
			cin>>a.a[i][j];
		}
	}
	int x;
	
	cout<<"请选择源点"<<endl;
	cin>>x;
	dijkstra(x);
	for(int i=0;i<n;i++)
	{
		cout<<dist[i]<<" ";
	}
}

2、多元哈夫曼编码问题

Problem Description
在一个操场的四周摆放着n堆石子。现要将石子有次序地合并成一堆。规定每次至少选2 堆最多选k堆石子合并成新的一堆,合并的费用为新的一堆的石子数。试设计一个算法,计算出将n堆石子合并成一堆的最大总费用和最小总费用。
对于给定n堆石子,计算合并成一堆的最大总费用和最小总费用。

Input
输入数据的第1 行有2 个正整数n和k(n≤100000,k≤10000),表示有n堆石子,每次至少选2 堆最多选k堆石子合并。第2 行有n个数(每个数均不超过 100),分别表示每堆石子的个数。

Output
将计算出的最大总费用和最小总费用输出,两个整数之间用空格分开。

Sample Input
7 3
45 13 12 16 9 5 22
Sample Output
593 199

代码如下:

//多元哈夫曼编码
//当构造WPL最小的树时,构造k路完全哈夫曼树
//的WPL最小,即如果点不够就用0节点补全,
//若要使构造树的WPL最大,就使用二路哈夫曼树,
//要选择值最大的两个数进行合并 
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
struct node {
	int x;
};
struct tmp1{
	bool operator ()(struct node a,struct node b)
	{
		return a.x>b.x;//小顶堆 
	}
};
struct tmp2{
	bool operator ()(struct node a,struct node b)
	{
		return a.x<b.x;//大顶堆 
	}
		
	
};
int main()
{
	int n;
	int y;
	cin>>n>>y;
	int u=(n-1)%(y-1);
	int u1;
	if(u==0)u1=0;
	else
	u1=y-u-1;//u1是添加的空归并段个数 
	struct node a[n+u1];
	for(int i=0;i<n;i++)
	{
		cin>>a[i].x;
	}
	for(int i=n;i<n+u1;i++)
	a[i].x=0;
	priority_queue<struct node,vector<struct node>,tmp1 > q;//小顶堆 
	priority_queue<struct node,vector<struct node>,tmp2 > p;//大顶堆
	int count1=0;
	int count2=0;
	for(int i=0;i<n+u1;i++)
	{
		q.push(a[i]);
	
	 } 
	
	 while(q.size()>0)
	 {
	 	int ans=0;
	 	struct node b[y];
	 	for(int i=0;i<y;i++)
	 	{
	 		b[i]=q.top();
	 		q.pop();
	 		ans+=b[i].x;

		 }
		 
		 struct node c;
		 c.x=ans;
		 if(q.size()!=0)
		 q.push(c);
		 count1+=ans;
		
	 }
	 cout<<count1<<endl;;
	 for(int i=0;i<n;i++)
	{
		p.push(a[i]);
	
	 } 
	 while(p.size()>0)
	 {
	 	int ans=0;
	 	struct node b[2];
	 	for(int i=0;i<2;i++)
	 	{
	 		b[i]=p.top();
	 		p.pop();
	 		ans+=b[i].x;

		 }
		 
		 struct node c;
		 c.x=ans;
		 if(p.size()!=0)
		 p.push(c);
		 count2+=ans;
		
	 }
	 cout<<count2<<endl;
 } 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值