算法初入门--枚举、递归。贪心

1.枚举

枚举法,也称作穷举,指的是从问题所有可能的解的集合中一一枚举各元素。题目中给定的检验条件判定哪些是无用的,哪些是有用的。

  优点是算法简单,在局部地方使用枚举法,效果十分好。缺点是运算量过大,当问题的规模变大的时候,循环的阶数越大,执行速度越慢。

例题:

百钱买白鸡问题:有一个人有一百块钱,打算买一百只鸡。到市场一看,公鸡一只3元,母鸡一只5元,小鸡3只1元,试求用100元买100只鸡,各为多少才合适?

解析:

根据题意可以得到方程组:3X+5Y+Z/3=100;X+Y+Z=100;

#include<iostream>
using namespace std;
int main()
{
	int x,y,z;
	for(x=0;x<=100;x++)
	{
		for(y=0;y<=100;y++)
		{
			for(z=0;z<=100;z++)
			{
				if(x+y+z==100&&3*x+5*y+z/3==100&&z%3==0)
					cout<<x<<" "<<y<<" "<<z<<endl;
			}
		}
	}
	return 0;
} 

优化:利用方程组3X+5Y+Z/3=100;X+Y+Z=100;可将Z约去,得到新方程组4X+7Y=100

#include<iostream>
using namespace std;
int main()
{
	int x,y,z;
	for(x=0;x<=25;x++)
	{
		y=100-4*x;
		if(y%7==0&&y>=0)
		{
			y=y/7;
			z=100-x-y;
			if(z%3==0&&3*x+5*y+z/3==100)
				cout<<x<<" "<<y<<" "<<z<<endl; 
		}
	}
	return 0;
} 

2.递推

递推算法是一种简单的算法,即通过已知条件,利用特定关系得出中间推论,直至得出结果的算法。

 

例题:斐波那契数列

因数学家列昂纳多·斐波那契以兔子繁殖为例子而引入,故又称为“兔子数列”。

fibonacci 数列定义:

n = 1,2 时,fib(n) = 1

n > 2 时,fib(n) = fib(n-2) + fib(n-1)

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584,……….

解析:

递归算法

#include<iostream>
using namespace std;
int fib(int n)
{
	if(n<=1)
		return n;
	else
		return fib(n-1)+fib(n-2);
}
int main()
{
	int n;
	cin>>n;
	cout<<fib(n);
	return 0;
}

优化

#include<iostream>
using namespace std;
int fib(int n)
{
	int a=0;int b=1;int c;
	for(int i=2;i<=n;i++)
	{
		c=a+b;
		a=b;
		b=c;
	}
	return c;
}
int main()
{
	int n;
	cin>>n;
	cout<<fib(n);
	return 0;
}

3.贪心算法

所谓贪心算法是指在对问题求解时,总是作出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,它所做出的仅是在某种意义上的局部最优解。

贪心算法没有固定的算法框架,算法设计的关键是贪心策略的选择。

例题:合并果子

现在又n堆果子,第i堆有ai个果子。现在要把这些果子合并成一堆,每次合并的代价是两堆果子的总果子数。求合并所有果子的最小代价。

解析:每次选取最少的两堆合并,直到剩一堆即可。

#include<iostream>
#define maxsize 100
using namespace std;
void sort(int *a,int low,int high)
{
	int i=low,j=high;
	int temp=a[i];
	if(low>high)
		return;
	while(i<j)
	{
		while(i<j&&a[j]>=temp)
			j--;
		if(i<j)
			a[i]=a[j];
		while(i<j&&a[i]<=temp)
			i++;
		if(i<j)
			a[j]=a[i];
	}
	a[j]=temp;
	sort(a,low,i-1);
	sort(a,i+1,high);
}
void merge(int a[],int n)
{
	sort(a,0,n-1);
	int ans=0;
	for(int i=1;i<n;i++)
	{
		a[i]+=a[i-1];
		ans+=a[i];
		int temp=a[i];
		 
		for(int j=i+1;j<n&&a[j]<a[i];j++)
		{
			a[j-1]=a[j];
			a[j]=temp;	
		} 
	}
	cout<<ans<<endl;
}
int main()
{
 	int a[maxsize];
 	int n,i;
 	cin>>n;
 	for(i=0;i<n;i++)
	 	cin>>a[i]; 
	merge(a,n);
	return 0; 
}

 

利用优先队列的方法优化

普通的队列是一种先进先出的数据结构,元素在队列尾追加,而在队列头删除。在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出的行为特征。通常采用堆数据结构来实现。

 

priority_queue

基本操作:

empty():如果队列为空,则返回真

pop():删除对顶元素,删除第一个元素

push():加入一个元素

size():返回优先队列中拥有的元素个数

top():返回优先队列对顶元素,返回优先队列中有最高优先级的元素

头文件:#include<queue>

声明方式:

1.普通方法

priority_queue<int> q;//通过操作,按照元素从大到小的顺序出队

priority_queue<int,vector<int>,greater<int>> q;//通过操作,按照元素从小到大的顺序出队

2、自定义优先级:

struct cmp {     

operator bool ()(int x, int y)     

{        

 return x > y;   // x小的优先级高       //也可以写成其他方式,如: return p[x] > p[y];表示p[i]小的优先级高

}

};

priority_queue<int, vector<int>, cmp> q;    //定义方法

//其中,第二个参数为容器类型。第三个参数为比较函数。

3、结构体声明方式:

struct node {     

int x, y;     

friend bool operator < (node a, node b)     

{         

return a.x > b.x;    //结构体中,x小的优先级高     

}

};

priority_queue<node>q;   //定义方法

//在该结构中,y为值, x为优先级。

//通过自定义operator<操作符来比较元素中的优先级。

//在重载”<”时,最好不要重载”>”,可能会发生编译错误

#include<iostream>
#include<queue>
using namespace std;
int main()
{
	priority_queue<int,vector<int>,greater<int> >p;
	int n,m;
	int a,b,result=0;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>m;
		p.push(m);
	}
	while(p.size()>=2)
	{
		int a=p.top();p.pop();
		int b=p.top();p.pop();
		result+=a+b;
		p.push(a+b);
	}
	cout<<result<<endl;
	return 0;
}

 

点赞