枚举

参考:https://blog.csdn.net/linj_m/article/details/17393031

枚举法

枚举法,本质上就是搜索算法。

根据算法的定义,可以发现该算法有如下特点:

(1)题目的答案是一个有穷的集合,即答案可以被一一列举出来;

(2)题目存在给定的约束条件,根据条件可以判断哪些答案符合要求,哪些答案不符合要求。

(3)算法存在循环运算,一般使用while循环实现。

基本思想:

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

优缺点:

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

采用枚举的方法进行问题求解,需要注意3个问题:

  • 简单数学模型,数学模型中变量数量尽量少,它们之间相互独立。这样问题解的搜索空间的维度就小,反应到程序代码中,循环嵌套的层次就会少。我们上面从3维优化到一维。
  • 减少搜索的空间。利用已有知识,缩小数学模型中各个变量的取值范围,避免不必要的计算。反应到程序代码中,循环体被执行的次数少
  • 采用合适的搜索顺序。对搜索空间的遍历顺序要与数学模型中的条件表达式一致。

例题解析

1.百钱买百鸡问题

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

根据题意我们可以得到方程组:

  3X + 5Y + Z/3 = 100;
  X + Y + Z = 100;
  (100 > X,Y,Z > 0, Z%3 == 0),根据这两个公式,我们可以写出最为简单的代码,一一列举所有解进行枚举

代码:


int x,y,z;
for( x = 0; x < 100; x++ )
   for( y = 0; y < 100 ; y++ )
       for( z = 0; z < 100; )
         {
           if( x + y + z == 100 && 3 * x + 5 * y + z / 3 == 100 )
                {
                  cout << x << " " << y << " " << z << endl;
                }
                z += 3;
            }

然而我们可以根据已知条件来进行优化代码,减少枚举的次数:

三种鸡的和是固定的,我们只要枚举二种鸡(x,y),第三种鸡就可以根据约束条件求得(z = 100 - x - y),这样就缩小了枚举范围。
另外我们根据方程特点,可以消去一个未知数,得到下面
4X + 7Y = 100;
X + Y + Z = 100;
(X,Y,Z > 0, Z%3 == 0),=>>    0 <= x < = 25因此代码可以优化为下面这样子:


for( x = 0; x <= 25; x++ )
{
            y = 100 - 4 * x;
            if( y % 7 == 0 && y >= 0 )
            {
                y /= 7;
                z = 100 - x - y;
                if( z % 3 == 0 && 3 * x + 5 * y + z / 3 == 100  )
                   cout << x << " " << y << " " << z << endl;
            }
}
 

例题2、生理周期问题

人生来就有三个生理周期,分别为体力、感情和智力周期,它们的周期长度为23天、28天和33天。每一个周期中有一天是高峰。在高峰这天,人会在相应的方面表现出色。例如,智力周期的高峰,人会思维敏捷,精力容易高度集中。因为三个周期的周长不同,所以通常三个周期的高峰不会落在同一天。对于每个人,我们想知道何时三个高峰落在同一天。对于每个周期,我们会给出从当前年份的第一天开始,到出现高峰的天数(不一定是第一次高峰出现的时间)。你的任务是给定一个从当年第一天开始数的天数,输出从给定时间开始(不包括给定时间)下一次三个高峰落在同一天的时间(距给定时间的天数)。例如:给定时间为10,下次出现三个高峰同天的时间是12,则输出2(注意这里不是3)。  
Input  
输入四个整数:p, e, i和d。 p, e, i分别表示体力、情感和智力高峰出现的时间(时间从当年的第一天开始计算)。d 是给定的时间,可能小于p, e, 或 i。 所有给定时间是非负的并且小于365, 所求的时间小于21252。    
当p = e = i = d = -1时,输入数据结束。  
Output  
从给定时间起,下一次三个高峰同天的时间(距离给定时间的天数),  一个一个去组合的。但是这样做是没有必要的,我们可以进行代码优化,在问题的数学模型中,有多个条件可以满足时(我们要满足3个条件),可以采用逐步减小搜索空间的方法提高计算的效率,依次按照条件一,条件二,。。进行搜索。在最初的搜索空间上,首先按照条件一就行判定,然后将符合条件一的搜索空间,作为下面条件的索索空间。代码可以优化成:

#include<iostream>
#include<cstdio>
using namespace std;
#define N 21252
int main(){
    int p,e,i,d,caseNo = 0;
    while(cin>>p>>e>>i>>d && p!=-1){
        ++caseNo;
        int k;
        for(k = d+1;(k-p)%23;++k); //先找k的最小公倍数
        for(;(k-e)%28;k+=23);
        for(;(k-i)%33;k+=23*28);
        cout<<"Case"<<caseNo<<
            ":the next triple peak occurs in "<<k-d<<"  days"<<endl;
    }
}

3.熄灯问题---参考例题:poj1222

特殊:这里就是考虑了局部地方,以每一行的情况去计算;

参考:https://blog.csdn.net/nnnnnnnnnnnny/article/details/51584247

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值