二进制枚举题集 入门篇 第一弹

星期七补星期一的课,所以今天是星期八,上次说过星期八写博客,今天就来了。
二进制枚举是比较偏的算法,知道的人不多。其底层原理就是通过二进制中的1和0代表每种物品选与不选的情况。如果需要选取的物品为20, 则它所以可能的情况为2的20次方,即1 048 576,所以二进制枚举只能用来处理数据量较小的题目,尽管比较局限,但是它处理这类题目的程序相较于搜索是比较容易写出的,而且也很好理解。

例题1蓝桥杯和为T

蓝桥杯和为T

问题描述
  从一个大小为n的整数集中选取一些元素,使得它们的和等于给定的值T。每个元素限选一次,不能一个都不选。
输入格式
  第一行一个正整数n,表示整数集内元素的个数。
  第二行n个整数,用空格隔开。
  第三行一个整数T,表示要达到的和。
输出格式
  输出有若干行,每行输出一组解,即所选取的数字,按照输入中的顺序排列。
  若有多组解,优先输出不包含第n个整数的;若都包含或都不包含,优先输出不包含第n-1个整数的,依次类推。
  最后一行输出总方案数。
样例输入
5
-7 -3 -2 5 9
0
样例输出
-3 -2 5
-7 -2 9
2

#include<iostream>
int a[25];
using namespace std;
int main() {
    int n, k, sum, cnt = 0;
    cin>>n;
    for(int i = 0; i < n; ++i) {
      cin>>a[i];
    }
    cin>>k;
    for(int i = 1; i < (1 << n); ++i) {
      sum = 0;
      for(int j = 0; j < n; ++j) {
        if(i & (1 << j)) {
          sum +=a[j];
        }
      }
      if(sum == k) {
        for(int j = 0; j < n; ++j) {
          if(i & (1 << j)) {
            cout<<a[j]<<' ';
          }
        }
        cout<<"\n";
        cnt++;
      }
    }
    cout<<cnt;
    return 0;
}

例题2 HDU吉姆的天平

HDU 吉姆的天平

Jam’s balance
Problem Description
Jim has a balance and N weights. (1≤N≤20)
The balance can only tell whether things on different side are the same weight.
Weights can be put on left side or right side arbitrarily.
Please tell whether the balance can measure an object of weight M.

Input
The first line is a integer T(1≤T≤5), means T test cases.
For each test case :
The first line is N, means the number of weights.
The second line are N number, i’th number wi(1≤wi≤100) means the i’th weight’s weight is wi.
The third line is a number M. M is the weight of the object being measured.

Output
You should output the “YES"or"NO”.

Sample Input

1
2
1 4
3
2
4
5

Sample Output

NO
YES
YES

题意:有一个天平,n个砝码,每次给出一个数k,问能否用天平秤出这个重量。
这题乍一看和第一题差不多,实际上有很多不同
题解:需要注意的就是砝码可以在天平两边放,类似于高中学的左码右物,然后游码往右偏移了一部分。举个例子比较好理解,比如有两个砝码 1, 3, 那么天平不仅可以秤出1,3,4,还能秤出3-1=2这个重量。

#include<iostream>
#include<cstring>
using namespace std;
const int N = 2005;
int a[N];
bool vis[N];
int main() {
    int t, m, n, k, sum;
    cin>>t;
    while(t--) {
      memset(vis,0,sizeof(vis));
      cin>>n;
      for(int i = 0; i < n; ++i)  cin>>a[i];
      for(int i = 1; i < (1<<n); ++i) {
        sum = 0;
        for(int j = 0; j < n; ++j) {
          if(i & (1 << j)) {
            sum+=a[j];
          }
        }
        vis[sum] = 1;
        for(int j = 0; j < n; ++j) {
          if(sum - a[j] >= 0)   vis[sum-a[j]] = 1;
        }
      }
      cin>>m;
      for(int i = 0; i < m; ++i) {
        cin>>k;
        if(vis[k]) puts("YES");
        else puts("NO");
      }
    }
    return 0;
}

例题3 poj 神奇的口袋

poj百炼 神奇的口袋

描述
有一个神奇的口袋,总的容积是40,用这个口袋可以变出一些物品,这些物品的总体积必须是40。John现在有n个想要得到的物品,每个物品的体积分别是a1,a2……an。John可以从这些物品中选择一些,如果选出的物体的总体积是40,那么利用这个神奇的口袋,John就可以得到这些物品。现在的问题是,John有多少种不同的选择物品的方式。
输入
输入的第一行是正整数n (1 <= n <= 20),表示不同的物品的数目。接下来的n行,每行有一个1到40之间的正整数,分别给出a1,a2……an的值。
输出
输出不同的选择物品的方式的数目。
样例输入

3
20
20
20

样例输出

3
#include<iostream>
int a[25];
using namespace std;
int main() {
    int n, cnt = 0, sum;
    cin>>n;
    for(int i = 0; i < n; ++i) {
      cin>>a[i];   
    }
    for(int i = 1; i < (1 << n); ++i) {
      sum = 0;
      for(int j = 0; j < n; ++j){
        if(i & (1 << j)) {
          sum += a[j];
        }
      }
      if(sum == 40) cnt ++;
    }
    cout<<cnt<<"\n";
    return 0;
}

例题4 nefu 趣味解题

nefu 趣味解题

Description

ACM程序设计大赛是大学级别最高的脑力竞赛,素来被冠以"程序设计的奥林匹克"的尊称。大赛至今已有近40年的历史,是世界范围内历史最悠久、规模最大的程序设计竞赛。比赛形式是:从各大洲区域预赛出线的参赛队伍,于指定的时间、地点参加世界级的决赛,由1个教练、3个成员组成的小组应用一台计算机解决7到13个生活中的实际问题。
现在假设你正在参加ACM程序设计大赛,这场比赛有 n 个题目,对于第 i 个题目你有 a_i 的概率AC掉它,如果你不会呢,那么这时候队友的作用就体现出来啦,队友甲有 b_i 的概率AC掉它, 队友乙有 c_i 的概率AC掉它,那么现在教练想知道你们队伍做出 x 个题目的概率。

Input

输入一个正整数T(T<=100),表示有T组数据,对于每组数据首先输入一个 n (7<=n<=13),表示有 n 个题目,接下来输入三行,
第一行输入 n 个数a_i,第二行输入 n 个数b_i,第三行输入 n 个数c_i, 其中 a_i, b_i, c_i 的意义如题,最后输入一个 x 表示教练想要知道你们队伍做出的题目数(x>=0)。

Output

输出一行表示结果,保留4位小数

Sample Input

2
7
0.1 0.2 0.3 0.4 0.5 0.6 0.7
0.2 0.3 0.4 0.5 0.6 0.7 0.8
0.3 0.4 0.5 0.6 0.7 0.8 0.9
1
7
0.1 0.2 0.3 0.4 0.5 0.6 0.7
0.2 0.3 0.4 0.5 0.6 0.7 0.8
0.3 0.4 0.5 0.6 0.7 0.8 0.9
5

Sample Output

0.0000
0.2811
#include<iostream> 
#include <iomanip>//输出格式用的头文件
using namespace std;
#define _for(n,m,i) for (register int i = (n); i < (m); ++i)//for循环的宏定义
const int N = 20;
double chose, p, a[N], b[N], c[N], Ac[N], Wr[N];
int main() {
    int t, n, x, cnt;
    cin>>t;
    while(t--) {
      cin>>n;
      _for(0, n, i) cin>>a[i];
      _for(0, n, i) cin>>b[i];
      _for(0, n, i) cin>>c[i];
      cin>>x;
      chose = 0;
      for(int i = 0; i < (1<<n); ++i) {
        p = 1;
        cnt = 0;
        _for(0, n, j) {
          Wr[j] = (1-a[j]) * (1-b[j]) * (1-c[j]);//做不出这题的概率
          Ac[j] = 1 - Wr[j];
          if(i & (1<<j)) {
            p *= Ac[j];
            cnt++;
          } else p *= Wr[j];
        }
        if(cnt==x) chose += p;
      }
      cout<<fixed<<setprecision(4)<<chose<<"\n";//输出四位小数,不足四位时用0补齐的写法,直接用printf也可
    }
}

例题5HDU权力指数

HDU 权利指数

Problem Description
在选举问题中,总共有n个小团体,每个小团体拥有一定数量的选票数。如果其中m个小团体的票数和超过总票数的一半,则此组合为“获胜联盟”。n个团体可形成若干个获胜联盟。一个小团体要成为一个“关键加入者”的条件是:在其所在的获胜联盟中,如果缺少了这个小团体的加入,则此联盟不能成为获胜联盟。一个小团体的权利指数是指:一个小团体在所有获胜联盟中成为“关键加入者”的次数。请你计算每个小团体的权利指数。

Input
输入数据的第一行为一个正整数T,表示有T组测试数据。每一组测试数据的第一行为一个正整数n(0<n<=20)。第二行有n个正整数,分别表示1到n号小团体的票数。

Output
对每组测试数据,在同一个行按顺序输出1到n号小团体的权利指数。

Sample Input

2
1
10
7
5 7 4 8 6 7 5

Sample Output

1

16 22 16 24 20 22 16

#include<iostream>
#include<cstring> 
using namespace std;
int a[22], b[22];
int main() {
    int t, n;
    cin>>t;
    while(t--) {
      cin>>n;
      int SumTicket = 0;
      for(int i = 0; i < n; ++i) {
        cin>>a[i];
        SumTicket += a[i];
      }
      memset(b, 0, sizeof(b));
      for(int i = 1; i < (1<<n); ++i) {
        int sum = 0;
        for(int j = 0; j < n; ++j) {
          if(i & (1<<j)) sum += a[j];
        }
        for(int j = 0; j < n; ++j) {
          if(i & (1<<j)) {
            if(sum > SumTicket/2) {//当这个团体在时,是获胜联盟
              if(sum-a[j] <= SumTicket/2) {//当去掉这个成员时不是
                b[j]++;
              }
            }
          }
        }
       } 
        for(int i = 0; i < n; ++i) {
          printf("%d%c",b[i], i == n-1 ? '\n' :' ');
        }
    }
} 
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值