贪心算法之近似算法(格雷厄姆算法)初识

       之前做了很多贪心算法,他们都能找到最优解,这也是之所以用贪心算法的原因。贪心算法较之其他,最大的优势体现在时间复杂度低,空间复杂度也比较低。对于试用贪心算法的题型,

有两个重要特征:贪心策略与最优子结构。贪心策略即每步采取策略的依据;最优子结构则是指问题的求解可以转化为求解子问题的最优解。这点与动态规划有点像,但后者要枚举问题的解空

间,资源消耗很大。

      贪心算法不一定保证得到最优解,但很多时候用其他方法的无效(有的是确实没有解决方法,有的是复杂度难以接受),在这种情况下我们可以尝试用近似算法,根据一定的有效贪心策略,

哪怕得不到最优解,但权衡之下也是可以接受的

      例如给定若干物品,要求尽可能的将它们分成质量相近的两堆。如物品数为5,重量分别为3,3,2,2,2,很容易根据经验判断分成3+3和2+2+2的两堆。但这是一个2^n级难题,数据量一大就出

现组合爆炸。解决该问题目前还没有无有效的方法。枚举法可以得到最优解,但时间复杂度为O(2^n),难以接受。下面是n<=15时的枚举法,用位操作简化计算。

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

const int MAXN=20;
int w[MAXN];
int used[MAXN];
const int INF=1<<30;
int n,id,sum;

int Solve()
{
	int min,cnt=1<<n;
	sum=0;
	for(int i=0;i<n;i++)
		sum+=w[i];
	min=INF;
	id=-1;

	for(int i=0;i<cnt;i++)
	{
		int tmp=0;
		for(int j=0;j<n;j++)
		{
			if(i&(1<<j))
				tmp+=w[j];
		}
		if(abs(sum-2*tmp)<abs(sum-2*min))
		{
			min=tmp;
			id=i;
		}
	}
	return min;
}

int main()
{
	cin>>n;
	memset(used,0,sizeof(used));
	for(int i=0;i<n;i++)
		cin>>w[i];

	int ans=Solve();
	for(int i=0;i<n;i++)
	{
		if(id&(1<<i))
			used[i]=true;
	}
	printf("\n第一堆为:");
	for(int i=0;i<n;i++)
		if(used[i])
			printf("%d ",w[i]);

	printf("\t合计 %d\n第二堆为:",ans);
	for(int i=0;i<n;i++)
		if(!used[i])
			printf("%d ",w[i]);

	printf("\t合计 %d\n",sum-ans);
	system("pause");
}
运行结果为:2+2+2=6   3+3=6


      格雷厄姆提出了解决该问题的近似算法。即每次从尚未分堆的物品中选择最大我w[i]的,然后分别将它试探性加到已分的两堆(a1,b1)中,若|a1+w[i]-b1|>|a1-w[i]-b1|,泽加到b1中;否则加到

a1中。已有神牛可以证明这样的最终结果与最优解的误差不超过16%。下面是格雷厄姆算法的实现。

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;

const int MAXN=20;
int w[MAXN];
int used[MAXN];
int n,a,b;

void Solve()
{
	sort(w,w+n);
	a=0,b=0;
	for(int i=n-1;i>=0;i--)
	{
		if(abs(a+w[i]-b)<=abs(a-w[i]-b))
		{
			a+=w[i];
			used[i]=true;
		}
		else b+=w[i];
	}
}

int main()
{
	cin>>n;
	memset(used,0,sizeof(used));
	for(int i=0;i<n;i++)
		cin>>w[i];

	Solve();

	printf("\n第一堆为:");
	for(int i=0;i<n;i++)
		if(used[i])
			printf("%d ",w[i]);

	printf("\t合计 %d\n第二堆为:",a);
	for(int i=0;i<n;i++)
		if(!used[i])
			printf("%d ",w[i]);

	printf("\t合计 %d\n",b);
	system("pause");
}
运行结果为:2+2+3=7  2+3=7 

        

          在有些情况下是完全可以接受近似算法的。

     

     

你可能感兴趣的:(贪心算法,近似算法,格雷厄姆算法)