算法是一个程序的灵魂。
学习算法时:
1. 概念
2. 核心思想
3. 程序框架
4. 优缺点
5. 应用场合
枚举算法
概念
依次列举出所有可能产生的结果, 根据题目中的条件对所得的结果进行逐一的判断,过滤掉那些不符合要求的, 保留那些符合要求的, 也可以称之为暴力算法。
基本思想
根据实际问题设计多重循环, 一一枚举所有可能的状态, 并用问题给定的约束条件检验哪些状态是需要的, 哪些状态是不需要的。 能使命题成立的状态, 即为其解。 虽然枚举法本质上属于搜索策略, 但是它与后面讲的回溯法或宽度优先搜索有所不同。
枚举法的条件
1. 可预先确定每个状态的元素个数n;
2. 可预先确定每个状态元素a1,a2,…,an的值域。
枚举法的框架(循环+选择):
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)满足检验条件)输出问题的解;
应用场合
并不是所有问题都可以使用枚举算法来解决,只有少数问题可以,只有当问题的所有解的个数不太多时,并在题目所给时间内能得到解,才可以使用枚举。
但是可以配合其他算法来进行使用,做小范围枚举、小规模枚举。
枚举法的优缺点
枚举法的优点:由于枚举算法一般是现实问题的“直译”,且是建立在考察大量状态、甚至是穷举所有状态的基础之上的,因此比较直观,易于理解,其算法的正确性也比较容易证明。
枚举法的缺点:枚举算法的效率取决于枚举状态的数量以及
单个状态枚举的代价,因此效率比较低。
例:砝码称重
【 问题描述】
设有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[1001]的数组来判重。
#include<bits/stdc++.h>
#include <iostream>
using namespace std;
int a[7],b[10001]={0};//b数组用于存储质量和情况,0表示没有被摆出来过,1表示被摆出来过
int main()
{
int i,j,k,m,n,o;
int tot=0;
cin>>a[1]>>a[2]>>a[3]>>a[4]>>a[5]>>a[6];
for(i=0;i<=a[1];i++)
for(j=0;j<=a[2];j++)
for(k=0;k<=a[3];k++)
for(m=0;m<=a[4];m++)
for(n=0;n<=a[5];n++)
for(o=0;o<=a[6];o++)
if(i+j+k+m+n+o!=0)
if(b[i*1+j*2+k*3+m*5+n*10+o*20]==0)
{
b[i*1+j*2+k*3+m*5+n*10+o*20]=1;
tot++;//总数加一
}
cout<<"Total="<<tot;//输出
return 0;
}
例:NOIP 2011 铺地毯
【 问题描述】
为了准备一个独特的颁奖典礼, 组织者在会场的一片矩形区域( 可看做是平面直
角坐标系的第一象限) 铺上一些矩形地毯。 一共有 n 张地毯, 编号从 1 到n。 现在
将这些地毯按照编号从小到大的顺序平行于坐标轴先后铺设, 后铺的地毯覆盖在
前面已经铺好的地毯之上。
地毯铺设完成后, 组织者想知道覆盖地面某个点的最上面的那张地毯的编号。 注
意: 在矩形地毯边界和四个顶点上的点也算被地毯覆盖。
【 输入格式】
输入共n+2 行。
第一行, 一个整数 n, 表示总共有n 张地毯。
接下来的 n 行中, 第i+1 行表示编号 i 的地毯的信息, 包含四个整数
a,b,g,k, 每两个整数之间用一个空格隔开, 分别表示铺设地毯的左下角的坐标(a,b)
以及地毯在x 轴和y 轴方向的长度。 第 2
n+2 行包含两个整数x 和y, 表示所求的地面的点的坐标(x,y)。
【 输出格式】
输出共1 行, 一个整数, 表示所求的地毯的编号; 若此处没有被地毯覆盖则输出-1.
【 输入样例】
31
0 2 3
0 2 3 3
2 1 3 3
2 2
【 输出样例】
3
【思路】从后往前枚举地毯( 因为后覆盖的地毯在上面, 而题目正好要求最上面的地毯) , 如果有一个地毯满足条件( 满足什么条件在下面讲解) 就直接输出, 并退出。 如果没有地毯满足条件, 就输出-1
#include<bits/stdc++.h>
#include<cstdio>
#include<iostream>
using namespace std;
int d[105][4];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>d[i][0]>>d[i][1]>>d[i][2]>>d[i][3];
}
int x,y;
cin>>x>>y;
int ans=0;
for(int i=n;i>=1;i--)
{
if(x>=d[i][0]&&x<=d[i][0]+d[i][2]&&y>=d[i][1]&&y<=d[i][1]+d[i][3])
{
ans=i;
break;
}
}
if(ans>0)
{
cout<<ans<<endl;
}
if(ans==0)
{
cout<<"-1"<<endl;
}
return 0;
}
枚举法的优化
枚举算法的时间复杂度:
状态总数*单个状态的耗时
主要优化方法:
⑴ 减少状态总数
⑵ 降低单个状态的考察代价
优化过程从以下几个方面考虑:
⑴ 枚举对象的选取
⑵ 枚举方法的确定
⑶ 采用局部枚举或引进其他算法