zoukankan      html  css  js  c++  java
  • P1036 选数题解

    题目传递门

    写在最前:
    暴搜是入门算法的必修课程,一般是青少年蓝桥杯赛的第4题左右的水平,如果做出来,在吉林省这样的省份,基本就是一等奖的水平。

    暴搜又分为深度优先和广度优先,其中入门级选手对于深度优先不好理解,我试图以最清晰的思路引领同学们突破这个问题。

    1、结束的条件

    深度优先搜索本质上是递归,递归必然要求有出口,否则就是死循环,所以,深度优先想要考虑出口是什么。

    2、参数的设置

    我们不是以第1个或最后1个做为思考对象,通常是以中间某个普遍的位置进行思考,一般称为第(i)个,或第(i)步,这里最关键的问题是参数有哪些!一般有一个位置或者步数,这是必备的。其它的根据题意来配置,比如有数量上限,就需要增加一个当前数量;比如有和是不是质数,就需要携带和这个概念。

    3、下步的选择

    我们试图找出(i)步和(i+1)步的关系。用一句短点的话来描述:“下一步才是我们可操作的!!
    比如:下一步可以通过一个循环的方法,尝试所有可能的,也可能像01背包一样,下一轮选择物品或者下一轮不选择物品,两种情况,演化成了两个分支,生个分支走到头,都会可能存在一组解,在本岔路口,需要把两组解加在一起才是本路口的所有解。

    下面,我们通过三个步骤来解这道题:

    1、结束的条件

    (1)选择完了所有的数字,对应(n),还没有找到和为质数。
    (2)已经选择够了(k)个数字,就需要判断和是不是质数了,是就输出不是也没有必要继续了。

    2、参数的设置

    (1) 走到第几个数面前,(step)
    (2) 目前的和是多少了,这个要求一路检查和是不是质数,不带和这个概念不行。---来自于题意
    (3) 一共(k)个数,现在用了几个了。---来自于题意

    3、下步的选择

    都是站在现在看未来,未来才是我们准备操作的关键,注意,不是现在啊!
    下一个数字,我们可以选择要,还是不要。这是两个分支,我们的任务就是把此子任务发给两个人,让他们分别去做,分别回来汇报,然后我们再把两个方向的数值加在一起,再向上汇报。

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 30;
    int a[N];
    int n, k;
    
    //是不是质数
    bool isPrime(int n) {
        if (n < 2) return false;
        for (int i = 2; i <= n / i; i++)
            if (n % i == 0) return false;
        return true;
    }
    
    
    /**
     * 功能:获取可行方法数
     * @param step  走在第几个箱子面前
     * @param sum   已经获得的数字和
     * @param m     已经选择了几个数字
     * @return      获取可行方法数
     */
    int dfs(int step, int sum, int m) {
        //边界状态
        if (step == n + 1) return 0;//都走冒了还没有停止,说明不可能有结果了
        if (m == k) return isPrime(sum);//已经完成选择k个数,判断和是不是质数.如果是质数,说明找到了一种合法解
    
        //解析:
        //1、dfs(step + 1, sum + a[step + 1], m + 1)
        //当前的箱子里面的数字要着,然后走到下一个箱子面前,这样,有三个东西发生了变化
        //分别是 面对的箱子是哪个,数字总和,已选择的总数
    
        //2、dfs(step + 1, sum, m)
        //当前的箱子里面的数字不要,然后走到下一个箱子面前,这样,有一个东西变生了变化
        //面对的箱子是哪个
        return dfs(step + 1, sum + a[step + 1], m + 1) + dfs(step + 1, sum, m);
    }
    
    int main() {
        cin >> n >> k;
        for (int i = 1; i <= n; i++) cin >> a[i];
        //之所以把step初始值设置为0,可以理解为提前观察,决策后分支.看好了再走路,而不是走了路再看看好不好走。
        cout << dfs(0, 0, 0);
        return 0;
    }
    
    

    二进制枚举的方法也粘贴上一个方法吧:

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 30;
    int a[N];
    
    //是不是质数
    bool isPrime(int n) {
        for (int i = 2; i <= n / i; i++)
            if (n % i == 0) return false;
        return true;
    }
    
    int main() {
        int n, k, ans = 0;
        cin >> n >> k;
        for (int i = 0; i < n; i++) scanf("%d", &a[i]);
    
        int U = 1 << n; //U-1即为全集 ,比如 1<<5 就是 2的5次方,就是32,U=32。而U-1=31,就是表示 1 1 1 1 1
        for (int S = 0; S < U; S++) { //枚举所有子集[0,U)
            //找到k元子集
            if (__builtin_popcount(S) == k) {
                int sum = 0;
                //是哪些数存在于子集中呢?
                for (int i = 0; i < n; i++) {
                    int bit = (S >> i) & 1;
                    if (bit) sum += a[i]; //遍历数字S的每一位,如果不是0,表示这一位上的数字是存在的,需要加进来
                }
                //判断子集元素和是不是素数
                if (isPrime(sum)) ans++;
            }
        }
        cout << ans << endl;
        return 0;
    }
    
  • 相关阅读:
    如何挑选牙膏--2019/10/20
    怎样选卫生纸-2019/10/20
    页面动态加入<script>标签并执行代码
    ss 各种浏览器兼容前缀写法
    nth-child(n)、first-child、last-child用法
    改变checkbox的默认样式
    border和outline的区别
    标签嵌套规则和注意事项
    物理尺寸 转换为 像素
    打印iframe内容
  • 原文地址:https://www.cnblogs.com/littlehb/p/15006108.html
Copyright © 2011-2022 走看看