• 浅谈枚举


    枚举谁都会,但是同样都是打枚举,得分就可能不一样,就比如砝码称重那道题,神仙涛枚举100分,我枚举30分,这就是枚举技巧,所以对于我这种算法基本靠枚举的蒟蒻来说学好枚举很重要了...咕咕咕

    定义

    枚举就是需要遍历每个解来寻找最优解/计数的问题 ,复杂度会出现指数级,此时数据范围一般较小 关键在于能否找到需要枚举的变量,以及枚举的效率,避免无用的枚举

    题目链接:

    P1036 选数

    思路:

    这道题就是个简单的搜索或者说是枚举,主要是想用这道题来浅谈一下枚举的技巧,当然每道题都不一样。

    最暴力的枚举:

    来自题解

    #include<stdio.h>
    #include<cmath>
    #include<cstdlib>
    #include<iostream>
    using namespace std;
    int a[21];
    int s,x,n,k;
    bool zs(long long y)//判断是否是质数
    {
        if (y==1||!y) return 0;
        for (int i=2;i<=sqrt(y);i++)
         if (!(y%i)) return 0;
        return 1;
    
    }
    void sr()
    {
        scanf("%d %d",&n,&k);
        for (int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            x+=a[i];//求下总和
        }
    }
    void js()
    {
        if (k==19||k==n-1) {for (int i=1;i<=n;i++) if (zs(x-a[i])) s++;return;}
        if (k==20||n==k) {if (zs(x)) s++;return;}//两个特判
        if (k==1)  
         {
          for (int i=1;i<=n;i++) 
           if (zs(a[i])) s++;//计算
         }
        if (k==2)  
         {
          for (int i=1;i<=n-1;i++) 
           for (int i1=i+1;i1<=n;i1++) 
            if (zs(a[i]+a[i1])) s++;//计算
            return;
         }
        if (k==3)  
         {
          for (int i=1;i<=n-2;i++) 
           for (int i1=i+1;i1<=n-1;i1++) 
            for (int i2=i1+1;i2<=n;i2++) 
             if(zs(a[i]+a[i1]+a[i2]))s++;//计算
             return;
         }
        if (k==4)  
         {
           for (int i=1;i<=n-3;i++) 
            for (int i1=i+1;i1<=n-2;i1++) 
             for (int i2=i1+1;i2<=n-1;i2++) 
              for (int i3=i2+1;i3<=n;i3++)
              if(zs(a[i]+a[i1]+a[i2]+a[i3]))s++;
              return;
         }
        if (k==5)  
         {
           for (int i=1;i<=n-4;i++) 
            for (int i1=i+1;i1<=n-3;i1++) 
             for (int i2=i1+1;i2<=n-2;i2++) 
              for (int i3=i2+1;i3<=n-1;i3++)
               for (int i4=i3+1;i4<=n;i4++)
              if(zs(a[i]+a[i1]+a[i2]+a[i3]+a[i4]))s++;
              return;
         }
        if (k==6)
         {
           for (int i=1;i<=n-5;i++)
            for (int i1=i+1;i1<=n-4;i1++) 
             for (int i2=i1+1;i2<=n-3;i2++) 
              for (int i3=i2+1;i3<=n-2;i3++)
               for (int i4=i3+1;i4<=n-1;i4++)
                for (int i5=i4+1;i5<=n;i5++)
              if(zs(a[i]+a[i1]+a[i2]+a[i3]+a[i4]+a[i5]))s++;
              return;
         }
        if (k==7)
         {
           for (int i=1;i<=n-6;i++) 
            for (int i1=i+1;i1<=n-5;i1++)
             for (int i2=i1+1;i2<=n-4;i2++) 
              for (int i3=i2+1;i3<=n-3;i3++)
               for (int i4=i3+1;i4<=n-2;i4++)
                for (int i5=i4+1;i5<=n-1;i5++)
                 for (int i6=i5+1;i6<=n;i6++)
              if(zs(a[i]+a[i1]+a[i2]+a[i3]+a[i4]+a[i5]+a[i6]))s++;
              return;
         }
        if (k==8)  
         {
           for (int i=1;i<=n-7;i++) 
            for (int i1=i+1;i1<=n-6;i1++) 
             for (int i2=i1+1;i2<=n-5;i2++) 
              for (int i3=i2+1;i3<=n-4;i3++)
               for (int i4=i3+1;i4<=n-3;i4++)
                for (int i5=i4+1;i5<=n-2;i5++)
                 for (int i6=i5+1;i6<=n-1;i6++)
                  for (int i7=i6+1;i7<=n;i7++)
              if(zs(a[i]+a[i1]+a[i2]+a[i3]+a[i4]+a[i5]+a[i6]+a[i7]))s++;
              return;
         }
        if (k==9)
         {
           for (int i=1;i<=n-8;i++) 
            for (int i1=i+1;i1<=n-7;i1++) 
             for (int i2=i1+1;i2<=n-6;i2++) 
              for (int i3=i2+1;i3<=n-5;i3++)
               for (int i4=i3+1;i4<=n-4;i4++)
                for (int i5=i4+1;i5<=n-3;i5++)
                 for (int i6=i5+1;i6<=n-2;i6++)
                  for (int i7=i6+1;i7<=n-1;i7++)
                   for (int i8=i7+1;i8<=n;i8++)
              if(zs(a[i]+a[i1]+a[i2]+a[i3]+a[i4]+a[i5]+a[i6]+a[i7]+a[i8]))s++;
              return;
         }
        if (k==10)
         {
           for (int i=1;i<=n-9;i++) 
            for (int i1=i+1;i1<=n-8;i1++) 
             for (int i2=i1+1;i2<=n-7;i2++) 
              for (int i3=i2+1;i3<=n-6;i3++)
               for (int i4=i3+1;i4<=n-5;i4++)
                for (int i5=i4+1;i5<=n-4;i5++)
                 for (int i6=i5+1;i6<=n-3;i6++)
                  for (int i7=i6+1;i7<=n-2;i7++)
                   for (int i8=i7+1;i8<=n-1;i8++)
                    for (int i9=i8+1;i9<=n;i9++)
              if(zs(a[i]+a[i1]+a[i2]+a[i3]+a[i4]+a[i5]+a[i6]+a[i7]+a[i8]+a[i9]))s++;
              return;
         }
        if (k==11)
         {
           for (int i=1;i<=n-8;i++) 
            for (int i1=i+1;i1<=n-7;i1++) 
             for (int i2=i1+1;i2<=n-6;i2++) 
              for (int i3=i2+1;i3<=n-5;i3++)
               for (int i4=i3+1;i4<=n-4;i4++)
                for (int i5=i4+1;i5<=n-3;i5++)
                 for (int i6=i5+1;i6<=n-2;i6++)
                  for (int i7=i6+1;i7<=n-1;i7++)
                   for (int i8=i7+1;i8<=n;i8++)
              if(zs(x-(a[i]+a[i1]+a[i2]+a[i3]+a[i4]+a[i5]+a[i6]+a[i7]+a[i8])))s++;//注意这里是用x去减
              return;
         }
        if (k==12)
         {
           for (int i=1;i<=n-7;i++) 
            for (int i1=i+1;i1<=n-6;i1++) 
             for (int i2=i1+1;i2<=n-5;i2++) 
              for (int i3=i2+1;i3<=n-4;i3++)
               for (int i4=i3+1;i4<=n-3;i4++)
                for (int i5=i4+1;i5<=n-2;i5++)
                 for (int i6=i5+1;i6<=n-1;i6++)
                  for (int i7=i6+1;i7<=n;i7++)
              if(zs(x-(a[i]+a[i1]+a[i2]+a[i3]+a[i4]+a[i5]+a[i6]+a[i7])))s++;
              return ;//以下都是用x去减,注意!
         }
        if (k==13)
         {
           for (int i=1;i<=n-6;i++) 
            for (int i1=i+1;i1<=n-5;i1++)
             for (int i2=i1+1;i2<=n-4;i2++) 
              for (int i3=i2+1;i3<=n-3;i3++)
               for (int i4=i3+1;i4<=n-2;i4++)
                for (int i5=i4+1;i5<=n-1;i5++)
                 for (int i6=i5+1;i6<=n;i6++)
              if(zs(x-(a[i]+a[i1]+a[i2]+a[i3]+a[i4]+a[i5]+a[i6])))s++;
              return;
         }
        if (k==14)
         {
           for (int i=1;i<=n-5;i++)
            for (int i1=i+1;i1<=n-4;i1++) 
             for (int i2=i1+1;i2<=n-3;i2++) 
              for (int i3=i2+1;i3<=n-2;i3++)
               for (int i4=i3+1;i4<=n-1;i4++)
                for (int i5=i4+1;i5<=n;i5++)
              if(zs(x-(a[i]+a[i1]+a[i2]+a[i3]+a[i4]+a[i5])))s++;
              return;
         }
        if (k==15)
         {
           for (int i=1;i<=n-4;i++) 
            for (int i1=i+1;i1<=n-3;i1++) 
             for (int i2=i1+1;i2<=n-2;i2++) 
              for (int i3=i2+1;i3<=n-1;i3++)
               for (int i4=i3+1;i4<=n;i4++)
              if(zs(x-(a[i]+a[i1]+a[i2]+a[i3]+a[i4])))s++;
              return;
         }
        if (k==16)
         {
           for (int i=1;i<=n-3;i++) 
            for (int i1=i+1;i1<=n-2;i1++) 
             for (int i2=i1+1;i2<=n-1;i2++) 
              for (int i3=i2+1;i3<=n;i3++)
              if(zs(x-(a[i]+a[i1]+a[i2]+a[i3])))s++;
              return;
         }
        if (k==17)
         {
           for (int i=1;i<=n-2;i++) 
            for (int i1=i+1;i1<=n-1;i1++) 
             for (int i2=i1+1;i2<=n;i2++) 
              if(zs(x-(a[i]+a[i1]+a[i2])))s++;
              return;
         }
        if (k==18)
         {
           for (int i=1;i<=n-1;i++) 
            for (int i1=i+1;i1<=n;i1++) 
             if (zs(x-(a[i]+a[i1]))) s++;//之前没有用x去减,给大家造成了一些困扰,在这里说声抱歉
             return;
         }
    }
    int main()
    {
       sr();
       js();
       printf("%d",s);
    }
    

    这方法说实话没谁了

    dfs枚举法:

    依次考虑每个元素是否选取 记录当前状态选取元素个数,以及被选取的元素之和 向下一层搜索时,更新状态,搜索结束时,还原状态

    /*
        原始的dfs
    */
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<vector>
    #include<map>
    #include<string>
    #include<cstring>
    #define MAXN 100000
    using namespace std;
    inline int read() {
    	char c = getchar();
    	int x = 0, f = 1;
    	while(c < '0' || c > '9') {
    		if(c == '-') f = -1;
    		c = getchar();
    	}
    	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x * f;
    }
    int ans;
    int a[MAXN];
    int n, k;
    int is_prime (int x)
    {
    	if(x==1) return 0;
    	for(int i=2;i*i<=x;++i)
    	{
    		if(x%i==0) return 0;
    	}
    	return 1;
    }
    void dfs(int now, int cnt, int s) {
        if (now == n) {
            if (cnt == k && is_prime(s)) ans++;
        } else {
            dfs(now + 1, cnt, s);
            dfs(now + 1, cnt + 1, s + a[now]);
        }
    }
    
    dfs(0, 0, 0);
    
    int main() {
    	cin>>n>>k;
    	for(int i=0;i<n;++i)
    	{
    		cin>>a[i];
    	}
    	dfs(0, 0, 0);
    	cout<<ans;
    	return 0;
    }
    
    

    观察上面的程序发现还是有问题的
    它仍然会搜索一些多余状态

    所以我们要剪枝

    剪枝版dfs

    剪枝如下,很显然是吧

    if (cnt > k || cnt + n - now < k) return;

    代码就不放了,太长了...只放核心:

    void dfs(int now, int cnt, int s) {
        if (now == n) {
            if (cnt == k && is_prime(s)) ans++;
        } else {
        	if (cnt > k || cnt + n - now < k) return;//这里**** 
            dfs(now + 1, cnt, s);
            dfs(now + 1, cnt + 1, s + a[now]);
        }
    }
    

    dfs枚举排列

    依次考虑每个位置选取的元素 记录当前状态已经被选取的元素 向下一层搜索时更新状态,搜索结束时,还原状态

    void dfs(int now) {
        if (now == n) {
            if (test(p)) ans++;
        } else {
            for (int i = 1; i <= n; i++) {
                if (!flag[i]) {
                    p[now] = i;
                    flag[i] = true;
                    dfs(now + 1);
                    flag[i] = false;
                }
            }
        }
    }
    dfs(0);
    

    高效枚举集合

    这个方法就比较牛逼了

    一个集合的子集个数是(2^n)个,而且,我们可以建立一个从(0)(2^n-1)的整数到集合子集之间的一一映射 具体来说,将最低位记作第(0)位,二进制表示第i位为(1),则表示取集合中的第(i)个元素 因此,只需枚举(0)(2^n-1)之间的整数即可 而判断某个元素有没有被选中,就是判断第(i)位是否位(a)

    for (int j = 0; j < (1 << n); j++) {
        int s = 0;
        for (int i = 0; i < n; i++) {
            if (j & (1 << i)) {
                s += a[i];
            }
        }
    }
    

    高效枚举排列

     #include <algorithm>
     std::next_permutation 
     std::prev_permutation 
    

    STL中的函数,接受两个参数表示数组的起止位置 会将数组变为对应的下/上一个排列 如果当前数组是最后一个排列,则变为第一个排列,并且返回false,否则返回true 时间复杂度(O(n))

    int p[MAXN];
    for (int i = 0; i < n; i++) p[i] = i + 1;
    do {
        test(p);
    } while (std::next_permutation(p, p + n));
    

    免费送一道next_permutation 的裸题
    P1088 火星人

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<vector>
    #include<map>
    #include<string>
    #include<cstring>
    using namespace std;
    inline int read() {
    	char c = getchar(); int x = 0, f = 1;
    	while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x * f;
    }
    int a[100560],n,m;
    signed main()
    {
    	cin>>n>>m;
    	for(int i=0;i<n;++i) cin>>a[i];
    	for(int i=1;i<=m;++i) next_permutation(a,a+n);
    	for(int i=0;i<n;++i)
    	{
    		cout<<a[i]<<" ";
    	}
    	return 0;
    }
    
  • 相关阅读:
    EF生成的SQL语句执行顺序问题。
    关于scope_identity()与 @@IDENTITY
    按条件设置gridcontrol 单元格属性
    DevExpress gridcontrol Master-Detail绑定到对象类型
    dev ChartControl 备忘
    gridcontrol 图片列异步加载
    关于EmitMapper,映射配置
    asp.net Hessian 服务的注册
    XtrasReport 标签打印
    Devexpress + wcf +ef 批量更新处理
  • 原文地址:https://www.cnblogs.com/pyyyyyy/p/10892739.html
Copyright © 2020-2023  润新知