• 试题 算法训练 和为T -> dfs 二进制枚举


    问题描述
      从一个大小为n的整数集中选取一些元素,使得它们的和等于给定的值T。每个元素限选一次,不能一个都不选。
    输入格式
      第一行一个正整数n,表示整数集内元素的个数。
      第二行n个整数,用空格隔开。
      第三行一个整数T,表示要达到的和。
    输出格式
      输出有若干行,每行输出一组解,即所选取的数字,按照输入中的顺序排列。
      若有多组解,优先输出不包含第n个整数的;若都包含或都不包含,优先输出不包含第n-1个整数的,依次类推。
      最后一行输出总方案数。
    样例输入
      5
      -7 -3 -2 5 9
      0
    样例输出
      -3 -2 5
      -7 -2 9
      2
    数据规模和约定
      1<=n<=22
      T<=maxlongint
      集合中任意元素的和都不超过long的范围
    思路: 实现指数型枚举 选和不选两种情况 
          先上dfs朴素版代码
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <stack>
    #include <cstring>
    #include <iomanip>
    #include <cmath>
    
    using namespace std;
    
    const int N = 25;
    
    int n, m, ans;
    int a[N], st[N];
    bool vis[N];
    
    void dfs(int u, int step, int s)
    {
        if (u == -1)
        {
            if (s == m && step >= 1)
            {
                ans ++ ;
                for (int i = step - 1; i >= 0; i -- )
                    cout << st[i] << ' ';
                cout << endl;
                
            }
            return ;
        }
    
        // 不选这个数
        dfs(u - 1, step, s);
        
        // 选这个数
        vis[u] = true;
        st[step] = a[u];
        dfs(u - 1, step + 1, s + a[u]);
        vis[u] = false;
        
    }
    
    int main()
    {
        cin >> n;
        for (int i = 0; i < n; i ++ ) cin >> a[i];
        cin >> m;
        dfs(n - 1, 0, 0);
        cout << ans << endl;
        return 0;
    }

      dfs+stl版本

    #include <bits/stdc++.h>
    using namespace std;
    vector<int> ans; //存储选了哪些数 
    int nums[30];  //存储输入的整数集
    int n, T;
    int cnt; //总方案数
    void dfs(int id, int sum) { //id表示当前遍历到的数的下标,sum表示当前已经选择的数的总和 
        if (id == -1) { //如果搜索完了 
            if (sum == T && ans.size() > 0) { //如果和为T且至少选了一个数 
                for (int i = ans.size() - 1; i >= 0; i--) {  
                    cout << ans[i] << " ";
                }
                cout << endl;
                cnt++;
            }
            return; 
        } 
        dfs(id - 1, sum); //不选这个数 
         
        ans.push_back(nums[id]); //选这个数,把这个数加入到ans中 
        dfs(id - 1, sum + nums[id]); //dfs下一层 
        ans.pop_back(); //回溯 
        
    }
    int main() {
        cin >> n;
        for (int i = 0; i < n; i++) {
            cin >> nums[i];
        }
        cin >> T;
        dfs(n - 1, 0); //从最后一个数开始往前搜索 
        cout << cnt << endl;
        return 0;
    }

    二进制枚举 同样也是控制选与不选

    二进制枚举子集:

      在二进制的每一位上只存在两种可能,一种是0一种是1,我们把0看做是不选此数字,把1看成是选择此数字。

      10011即为选择第一个和第四第五个数字,其余数字不选。

    for ( int i  =  0 ; i  <  ( 1  <<  n ) ;  i++) ;

      我们可以通过这个for循环得到每种情况

      接下来就是判断每个位置上的数字情况了(利用按位与运算&)

    按位与运算规则:0&0=0;  0&1=0;   1&0=0;    1&1=1;
    
    左移<<1<<0=1(1);
    
    1<<1=2(10);
    
    1<<2=4(100);
    
    1<<3=8(1000);
    
    1<<4=16(10000);
    
    ...
    
    1<<7=128(10000000);
    
    ...

    我们可以通过将每一种情况对应的二进制数字与    1与不同的数字左移后得到的数字   进行按位与运算

    10011  与   1左移零位后得到的数字进行按位与   10011&1 结果为1即第五位被选中 即是下标为0的数组元素被选中

    10011  与   1左移一位后得到的数字进行按位与   10011&100 结果为1即第四位被选中  即是下标为1的数组元素被选中

    10011  与   1左移二位后得到的数字进行按位与   10011&100 结果为0即第三位未被选中  即是下标为2的数组元素未被选中

    10011  与   1左移三位后得到的数字进行按位与   10011&1000 结果为0即第二位未被选中  即是下标为3的数组元素未被选中

    10011  与   1左移四位后得到的数字进行按位与   10011&10000 结果为1即第一位被选中  即是下标为4的数组元素被选中

    将被选中的数字进行相加,看是否等于输入要求的数字,如果等于即输出结果,并将结果数加一。

    将每一种情况进行此运算即可得到全部的结果。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <stack>
    #include <cstring>
    #include <iomanip>
    #include <cmath>
    
    using namespace std;
    
    const int N = 25;
    
    int n, m, ans;
    int a[N], st[N];
    bool vis[N];
    
    int main()
    {
        cin >> n;
        for (int i = 0; i < n; i ++ ) cin >> a[i];
        cin >> m;
        for (int i = 0; i < (1 << n); i ++ ) // 从0~2^n-1个状态
        {
            int num = 0;
            for (int j = 0; j < n; j ++ ) // 遍历二进制的每一位
                if (i >> j & 1) // 判断二进制第j位是否存在
                    num += a[j]; // 如果存在输出第j个元素
            if (num == m && i != 0) // 不存在都不选的情况 所以i!=0
            {
                for (int j = 0; j < n; j ++ )
                    if (i >> j & 1)
                        cout << a[j] << ' ';
                cout << endl;
                ans ++ ;
            }
        }
        cout << ans << endl;
        return 0;
    }

     

  • 相关阅读:
    redis几种数据类型以及使用场景
    Ubuntu16.04安装redis和php的redis扩展
    详细透彻解读Git与SVN的区别(集中式VS分布式)
    bootstrap轮播如何支持移动端滑动手势
    vue添加cnzz统计访问量
    el-tabs值修改时更新路由参数值
    Vue ,elementUI,dropdown组件中command方法添加额外参数的方法
    vue页面滚动监听
    mintui tabbar底部跳转页面
    vue 引入bootstrap
  • 原文地址:https://www.cnblogs.com/zbx2000/p/12757455.html
Copyright © 2020-2023  润新知