枚举算法

本文详细介绍了枚举算法的基本思想、适用条件、框架结构,通过多个例题展示了如何运用枚举法解决数字统计、砝码称重、防护伞等问题,并讨论了枚举法的优缺点。文章还探讨了枚举算法的优化方法,如减少状态总数和降低单个状态的考察代价,以及在火柴棒等式问题上的具体优化实践。
摘要由CSDN通过智能技术生成

一、枚举法的基本思想

枚举法又称穷举法。

基本思想是根据提出的问题枚举所有可能状态,并用问题给定的约束条件检验哪些状态是需要的,哪些状态是不需要的。

能使命题成立的状态,即为其解。

枚举结构:循环+判断语句。

二、枚举法的条件

适合于枚举法求解的问题必须满足以下两个条件:

可预先确定每个状态的元素个数n。

如百钱买百鸡,状态元素可预先确定。

状态元素a1,a2,…,an的可能值为一个连续的值域

如果值域为实数级,每两个值之间有无穷个数,这就不能枚举出所有可能的值。

对于满足以上两个条件的问题,采用枚举法时,可以先确定枚举对象、枚举范围和判定条件,再一一枚举所有可能的解,验证是否是问题的解。

三、枚举法的框架结构

设ai1—状态元素ai的最小值;aik—状态元素ai的最大值(1≤i≤n),即a11≤a1≤a1k,a21≤a2≤a2k, ai1≤ai≤aik, ……,an1≤an≤ank for(a1=a11;a1<=a1k;a1++)

for(a1=a11;a1<=a1k;a1++)
     for(a2=a21;a2<=a2k;a2++)
      .....
         for(ai=ai1;ai<=aik;ai++)
             .....
                for(an=an1;an<=ank;an++)
                       if(状态(a1,...,ai...,an)满足检验条件)
                                        输出问题的解;

  例题1:数字统计(2010普及)

请统计某个给定范围[L, R]的所有整数中,数字 2 出现的次数。

比如给定范围[2, 22],数字 2 在数 2 中出现了 1 次,在数 12 中出现 1 次,在数 20 中出现 1 次,在数 21 中出现 1 次,在数 22 中出现 2 次,所以数字 2 在该范围内一共出现了 6次。

输入输出格式

输入格式:

输入共 1 行,为两个正整数 L 和 R,之间用一个空格隔开。

输出格式:

输出共 1 行,表示数字 2 出现的次数。

输入输出样例

【输入样例1】 2 22 【输入样例2】 2 100

【输出样例1】 6 【输出样例2】 20

cin>>m>>n;
ans=0;
for(i=m;i<=n;i++)//枚举n-m+1个状态
{    k=i;
     while(k)//
     {    if(k%10==2)ans++;
          k=k/10;
     }
}
cout<<ans<<endl;

例题2:砝码称重(NOIP1996)

【问题描述】设有1g、2g、3g、5g、10g、20g的砝码各若干枚(其总重<=1000),求用这些砝码能称出不同的重量的个数。 【输入】    

     输入1g、2g、3g、5g、10g、20g的砝码个数

【输出】  

     能称出不同的重量的个数

【输入样例】        

1 1 0 0 0 0

【输出样例】        

3

题目分析

根据输入的砝码信息,每种砝码可用的最大个数是确定的,而且每种砝码的个数是连续的,能取0到最大个数,所以符合枚举法的两个条件,可以使用枚举法。 枚举时,重量可以由1g,2g,……,20g砝码中的任何一个或者多个构成,枚举对象可以确定为6种重量的砝码,范围为每种砝码的个数。判定时,只需判断这次得到的重量是新得到的,还是前一次已经得到的,即判重。由于重量<=1000g,所以,可以开一个a[1005]的数组来判重,当得到x重量时,把a[x]置为1,下次再得到x时,还是置1,最后只需遍历一下a数组,即可得到重量的个数。算法实现时,伪代码如下:

memset(a,0,sizeof(a));  
cin>>a>>b>>c>>d>>e>>f;
for(c1=0;c1<=a;c1++) //1g砝码的个数
    for(c2=0;c2<=b;c2++) //2g砝码的个数
         for(c3=0;c3<=c;c3++) //3g砝码的个数
              for(c4=0;c4<=d;c4++) //5g砝码的个数
                   for(c5=0;c5<=e;c5++) //10g砝码的个数
                        for(c6=0;c6<=f;c6++) //20g砝码的个数
                        {   sum=c1*1+c2*2+c3*3+c4*5+c5*10+c6*20;
                             a[sum]=1; //标记
                        }
for(i=1;i<=1000;i++)if(a[i])num++; //统计不同重量的个数
cout<<num<<endl;

例题3:防护伞

【题目简述】

平面上有n个点,现要以其中一个点为圆心画一个圆,使得这个圆能够覆盖所有的点,求最小圆的面积。

样例输入

3

0  1

-8  -4

-1  4

样例输出

279.6017

【注意】 精确到小数点后 4 位 π=3.1415926535

【数据范围】  

对于50%的数据:2 <= N <= 100  

对于100%的数据:2<=N<=1000,-10000<=x,y<=10000

题目分析:

依次枚举每一个点i,计算出点i与所有点j的距离dist[i,j];

则以i为圆心覆盖所有点的圆的半径为:                  

R[i]=max{dist[i,j]|1<=j<=N}

因为要求圆的面积最小,即圆的半径最小,所以答案Ans=min{R[i]|1<=i<=N}。

【时间复杂度】O(N^2)

double x[1005],y[1005];
int main(){
	int i,j,n;
	double Max,Min=800000000,Pi=3.1415926535,dis;
	cin>>n;
	for(i=0;i<n;i++)
		cin>>x[i]>>y[i];
	for(i=0;i<n;i++){//枚举每一个点
		Max=0;
		for(j=0;j<n;j++) //找到这个点和其他点的最大距离
			if(i!=j){
				dis=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
				if(dis>Max)Max=dis;
			}
		if(Max<Min)Min=Max;//在所有的最大距离中找一个最小值
	}
	printf("%.4lf",Pi*Min);
	return 0;
}

四、枚举法的优缺点

枚举法的优点:

           比较直观,易于理解,其算法的正确性也比较容易证明。

枚举法的缺点:  

        当枚举的状态很多时,所用时间非常大,效率比较低。

 

五、枚举算法的优化

枚举算法的时间复杂度:状态总数*单个状态的耗时

主要优化方法:

      ⑴ 减少状态总数

      ⑵ 降低单个状态的考察代价

优化过程从以下几个方面

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值