2023年省信息教师培训day16(枚举)

算法是一个程序的灵魂。
学习算法时:
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;
}

枚举法的优化

枚举算法的时间复杂度:
状态总数*单个状态的耗时
主要优化方法:
⑴ 减少状态总数
⑵ 降低单个状态的考察代价
优化过程从以下几个方面考虑:
⑴ 枚举对象的选取
⑵ 枚举方法的确定
⑶ 采用局部枚举或引进其他算法

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值