zoukankan      html  css  js  c++  java
  • 暴力枚举法总结

    集训快要结束了,按照要求需要写一篇关于枚举的总结,于是在网上也看了许多其他菊苣写的文章,深受启发,但是思来想去感觉又不太系统,于是希望能在吸收那些知识后做一些整理,帮助后面的新人。

    枚举的基本方法:

      枚举,枚举,顾名思义,就是将所有情况都举出,并判断其是否符合题目条件。所以枚举的基本方法便是分析题意后,找到一个合适的维度列举每一个元素,以完成题目。其中如何找到一个合适的维度来进行枚举便是其中的最大难点。

    枚举的基本条件:

      首先是时间条件。一般来说主流的OJ当中,1000ms的时间限制下可以运行操作数为10^7以内的运算(通常10^6以内较为保险),所以在采用枚举方法之前最好看一下数据范围,确保整个程序的执行操作数不会超过10^6-10^7这个量级,如果超过了就尝试更换枚举的维度或者使用其他算法吧。

      其次是编程上的实现条件。在编程实现上,一般来说暴力枚举需要两个条件,一是枚举的范围一般需要连续,如果枚举范围是离散的,那么一般很难使用for循环枚举出所有状态,也就不能保证解的完整性(不过有些时候数据看似离散,但实际上可以经过处理变得连续)。第二个条件是枚举内容需要已知,不能在枚举到某个地方的时候出现未知(不过这个一般都被满足)。

    枚举的优点:

      1.能举出所有情况,保证解为正确解。

      2.能解决许多用其他算法难以解决的问题。

      3.便于思考与编程。

    例题一:火柴棒等式:

    【问题描述】给你n根火柴棍,你可以拼出多少个形如“A+B=C”的等式?等式中的A、B、C是用火柴棍拼出的整数(若该数非零,则最高位不能是0)。用火柴棍拼数字0-9的拼法如图所示:

    注意:

         1. 加号与等号各自需要两根火柴棍

         2. 如果A≠B,则A+B=C与B+A=C视为不同的等式(A、B、C≥0)

         3. n根火柴棍必须全部用上

    【输入】输入一个整数n(n≤24)。

    【输出】输出能拼成的不同等式的数目。 

    问题简述:给你n(n<=24)根火柴棒,叫你拼出 “A + B = C”这样的等式,求方案数。

    思路:由于题目中已经给出,最多有24根火柴,而等号和加号各用4根的前提下,ABC三个数则总共只有20根火柴,数据范围较小,可以用枚举法枚举A、B。这个时候我们发现,0-9这10个数字所用的火柴数为:6,2,5,5,4,5,6,3,7,6,很明显数字1用的火柴棒最少只要2根,不妨让B为1,那么A和C最多可以使用18根火柴,而C>=A,满足条件的A的最大取值为1111。所以枚举A和B的范围是从0~1111。

    为了加快速度,可以将0到2222的所有整数需要的火柴棒数目提前算好保存在数组中。
     
    代码:
    #include <iostream>
    using namespace std;
    int a[2223]={6,2,5,5,4,5,6,3,7,6};
    const int b[10]={6,2,5,5,4,5,6,3,7,6};
    //计算自然数n所需要的火柴数
    int need(int n)
    {
        int tmp, num;
        num=0;
        if(n==0) return 6;
        while(n>0) {
            tmp=n%10;
            num+=b[tmp];
            n/=10;
        }
        return num;
    }
    
    int main( )
    {
        int n,A,B,C,D,sum;
        cin>>n;
        sum=0;
        for(int i=10; i<2223; i++) //预处理
            a[i]=need(i);
        for(int i=0; i<=1000; i++) {
            for(int j=0; j<=1000; j++) {
                A=a[i];   B=a[j];  C=n-4-A-B;
                D=a[i+j];
                if(D==C) sum++;
            }
        }
        cout<<sum<<endl;
        return 0;
    }

    提示:本题使用枚举的优势在于数据范围较小,而且没有合适的其他算法来处理。

    例题二:计算几何你瞎暴力(玲珑OJ1143)

    DESCRIPTION

    今天HHHH考完了期末考试,他在教学楼里闲逛,他看着教学楼里一间间的教室,于是开始思考:

    如果从一个坐标为 (x1,y1,z1)(x1,y1,z1)的教室走到(x2,y2,z2)(x2,y2,z2)的距离为 |x1x2|+|y1y2|+|z1z2||x1−x2|+|y1−y2|+|z1−z2|

    那么有多少对教室之间的距离是不超过RR的呢?

    INPUT
    第一行是一个整数T(1T10)T(1≤T≤10), 表示有TT组数据 接下来是TT组数据,对于每组数据: 第一行是两个整数n,q(1n5×104,1q103)n,q(1≤n≤5×104,1≤q≤103), 表示有nn间教室, qq次询问. 接下来是nn行, 每行3个整数xi,yi,zi(0xi,yi,zi10)xi,yi,zi(0≤xi,yi,zi≤10),表示这间教室的坐标. 最后是qq行,每行一个整数R(0R109)R(0≤R≤109),意思见描述.
    OUTPUT
    对于每个询问RR输出一行一个整数,表示有多少对教室满足题目所述的距离关系.
    SAMPLE INPUT
    1 3 3 0 0 0 1 1 1 1 1 1 1 2 3
    SAMPLE OUTPUT
    1 1 3
    HINT
    对于样例,1号教室和2号教室之间的距离为3, 1号和3号之间的距离为3, 2号和3号之间的距离为0

    题意:在一个三维空间中有N个点,q次查询,每次查询给一距离r,求出三维空间中有多少对点之间的哈密顿距离小于r。

    思路:一开始的时候如果按照朴素的想法,先离线处理,两两配对求出每两个点之间的距离,之后输出,但是本题中点的数目n的数据较大,如果要全部处理的话需要109左右的操作数,肯定会超时。那么这个时候我们仔细观察后发现,每一个点的范围很小,0<=x,y,z<=10,如果我们通过坐标来遍历每一个点,那么就只需要10^3的复杂度,显然更合适。所以本题也是如此,通过以坐标为单位的枚举,就可以得到最后的结果:

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int MAX = 10005;
    const int MOD = 1e9+7;
    const int INF = 0x3f3f3f3f;
    
    int n, q, t, tem;
    int a, b, c, x, y, z;
    LL aa[35];
    LL dex[15][15][15];
    
    int dis(int aa, int bb, int cc, int xx, int yy, int zz)
    {
        return abs(aa-xx)+abs(bb-yy)+abs(cc-zz);
    }
    
    int main()
    {
        scanf("%d",&t);
        while(t--)
        {
            memset(aa, 0, sizeof(aa));
            memset(dex, 0, sizeof(dex));
            scanf("%d%d",&n,&q);
            while(n--)
            {
                scanf("%d%d%d",&x,&y,&z);
                ++dex[x][y][z];
            }
            for(a = 0; a <= 10; ++a)
                for(b = 0; b <= 10; ++b)
                    for(c = 0; c <= 10; ++c)
                        if(dex[a][b][c])
                            for(x = 0; x <= 10; ++x)
                                for(y = 0; y <= 10; ++y)
                                    for(z = 0; z <= 10; ++z)
                                        if(dex[x][y][z])
                                        {
                                            tem = dis(a, b, c, x, y, z);
                                            if(tem == 0)
                                                aa[tem] += (dex[x][y][z])*(dex[x][y][z]-1)/2;
                                            else
                                                aa[tem] += dex[x][y][z]*dex[a][b][c];
                                        }
            for(int i = 1; i <= 30; ++i)
                aa[i] /= 2;
            for(int i = 1; i <= 30; ++i)
                aa[i] += aa[i-1];
            while(q--)
            {
                scanf("%d",&tem);
                if(tem > 30)
                    tem = 30;
                printf("%lld
    ",aa[tem]);
            }
        }
        return 0;
    }

    提示:本题采用枚举是因为数据范围的独特性,当数据范围较小的时候,使用枚举的办法是一种好的办法。

  • 相关阅读:
    Bayes分类器原理分析以及实现
    数据库视图探究
    请求页式存储管理系统实验
    离散数学知识点整理(一)
    数据结构知识点总结之树
    数据结构知识点总结之栈、队列
    数据结构知识点总结之串、数组、广义表
    数据结构知识点总结之线性表
    数据结构知识点总结之绪论
    数据结构知识点总结
  • 原文地址:https://www.cnblogs.com/87hbteo/p/7235193.html
Copyright © 2011-2022 走看看