acm新手小白必看系列之(4)——超级简单的二进制枚举精讲及例题

acm新手小白必看系列之(4)——超级简单的二进制枚举精讲及例题

- 二进制操作(事前须知)
(还有排列组合知识要知道,比如n个数的子集个数是2的n次幂)
算数位运算:

1、与(&):
对于指定的两个数A=60(0011 1100)
B=13(0000 1101)
执行一下操作 A&B=12(0000 1100)
就是对二进制每一位进行了一次与操作,同为1,结果为1,否则为0
2、或(|):
对于指定的两个数A=60(0011 1100)
B=13(0000 1101)
执行一下操作 A|B=61(0011 1101)
就是对二进制每一位进行了一次或操作,同为0,结果为0,否则为1
3、非 按位取反(~):
对于指定的一个数A=60(0011 1100)
执行以下操作 ~A=195(1100 0011)//此处只举例了8位,int是32位的,且第一位是符号位
就是对二进制每一位进行了一次取反操作,若二进制数位0,则变成1,否则变成0.
4、异或运算(看黑体字就够了)
异或,英文为exclusive OR,缩写成xor
异或(xor)是一个数学运算符。它应用于逻辑运算。异或的数学符号为“⊕”,
计算机符号为“xor”。其运算法则为:a⊕b = (¬a ∧ b) ∨ (a ∧¬b)
如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
异或也叫半加运算,其运算法则相当于不带进位的二进制加法:
二进制下用1表示真,0表示假,则异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1),这些法则与加法是相同的,只是不带进位。
对于异或一个非常重要的性质,对于一个值异或同一个值两次,则结果还是原值。
在c/c++中异或用^符号表示;例如:对于指定的两个数 A=60(0011 1100) B=13(0000 1101)
执行一下操作 A^B=49(0011 0001)
就是对二进制每一位进行了一次异或操作,即非进位加法

  • 好了,下面开始准备WA

1.超级简单的一道题((当作例题)
给出长度为n的数组,求能否从中选出若干个,使他们的和为K.如果可以,输出:Yes,否则输出No
Input
第一行:输入N,K,为数组的长度和需要判断的和(2<=N<=20,1<=K<=10^9)
第二行:N个值,表示数组中元素的值(1<=a[i]<=10^6)
Output
输出Yes或No
Sample Input
5 13
2 4 6 8 10
Sample Output
No

#include <iostream>
#include <bits/stdc++.h>
using namespace std;

int main()
{
    int f=1,n,k,a[21];
    while(~scanf("%d %d",&n,&k))
    {
        for(int i=0; i<n; i++)
        {
            scanf("%d",&a[i]);
        }
       //二进制枚举的核心来了
        for(int i=0; i<(1<<n); i++)//第一个循环i<(1<<n)枚举出n个数每个取与不取的情况,1取,0不取.so,要来个长度为n的0.1串表示取与不取的情况。
        {
            int sum=0;
            for(int j=0; j<n; j++)
//第二个for循环是根据第一个循环枚举的0.1串长度来对这次枚举的0,1串判断,各个位上是否为1,就是是否取此数
            {
                if(i&(1<<j))//1<<j是得到某个字符串上只有一个位置为1的数,这个数再进行&,&操作的目的是与第一次枚举的i同为1的才得到1,才可以进入下面的if,也就是取到此数。然后第二个循环就可以判断出枚举到i这个01串哪些位置上为1
                {
                    sum+=a[j];
                }
            }
            //以上及是核心步骤,二进制的精华模板
            if(sum==k)
            {
                cout<<"Yes"<<endl;
                f=0;
                break;
            }
        }
        if(f==1)
        {
            cout<<"No"<<endl;
        }
    }
    return 0;
}

2.扑克练习
给你一些扑克,每张都对应一个点数,分别对应1-13,K 就是13;J 是11;Q是12;
现在想从这些扑克牌中取出一些牌,让这些牌的点数的和等于一个幸运数值P,问有多少种方案?
Input
输入数据第一行为n和p,分别代表n张扑克牌和幸运数(1<=n<=20,p<=260)
接下来是这n张牌的点数; 1<=点数<=13;
Output
输出能得到P 的方案数?
Sample Input
5 5
1 2 3 4 5
Sample Output
3

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int main()
{
    int a[21],n,p,ans,sum;
    while(~scanf("%d%d",&n,&p))
    {
        for(int i=0; i<n; i++)
        {
            scanf("%d",&a[i]);
        }
        ans=0;
        for(int i=0; i<(1<<n); i++)//套用
        {
            sum=0;
            for(int j=0; j<n; j++)
            {
                if(i&(1<<j))//套用

                    sum+=a[j];
            }
            if(sum==p)//在第一层里面判断点数之和
                ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}
  • 有信心了哈。。。。

3.老师开车
陈老师经常开车的行走,假设刚开始油箱里有T升汽油,每看见加油站陈老师就要把汽油的总量翻倍(就是乘2);每看见十字路口气油就要减少1升;最后的时候陈老师的车开到一个十字路口,然后车就没油了------就熄火了,陈老师好痛苦啊~~~!
然后他就开始回忆,一路上一共遇到5个加油站,10个十字路口,问造成这种惨烈的境遇有多少种可能?

Input
输入一个T ,(1<=T<=100);
Output
输出可能的方案数。
Sample Input
1
Sample Output
10

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int t,tmp,ans,k1,k2,i,j;
    cin>>t;
    ans=0;
    for(i=0; i<(1<<15); i++)//n可以理解为变成了15
    {
        tmp=t;
        k1=k2=0;
        for(j=0; j<15; j++)//套用模板
        {
            if(i&(1<<j))//套用模板
            {
                tmp=tmp*2;//加油
                k1++;//计数加油站
            }
            else
            {
                tmp--;//十字路口减一
                k2++;//计数十字路口
                if(tmp==0)break;//判断条件没有油了
            }
        }
        if(k1==5&&k2==10&&tmp==0)//判断条件
            ans++;
    }
    printf("%d\n",ans);
    return 0;
}

4.进阶题目权力的指数
在选举问题中,总共有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 <bits/stdc++.h>
using namespace std;
int n,t,i,j,s,tmp,a[21],ans[21],flag[21];
int main()
{
    cin>>t;
    while(t--)//循环t次的简便写法
    {
        cin>>n;
        s=0;
        for(i=0; i<n; i++)
        {
            cin>>a[i];
            s+=a[i];//求总票数
        }
        memset(ans,0,sizeof(ans));
        /*讲解一下sizeof函数,它和strlen函数类似,但是区别是strlen函数遇到“\0”之前的所有字符串长度
        而sizeof是声明变量之后所占的内存数,不是实际长度*/
        /*萌新可能又问了,memset是啥啊,官方解释是用来对一段内存空间全部设置为某个字符,其实多用与对定义的字符串进行初始化“ ”或者“\0”,memset方便清空一个结构类型的变量或者数组,特方便*/
        for(i=0; i<(1<<n); i++)//开始枚举
        {
            tmp=0;
            memset(flag,0,sizeof(flag));//把长度为sizeof(flag)的flag置为0,就是置为中间的数
            for(j=0; j<n; j++)
            {
                if(i&(1<<j))//套模板
                {
                    tmp=tmp+a[j];//m个团体的总票数
                    flag[j]=1;//加一个tmp就标记一下
                }
            }
            if(tmp<=s/2)//少一个团体就当不上
            {
                for(j=0; j<n; j++)
                {
                    if(tmp+a[j]>s/2&&flag[j]==0)//加上某个团体就当上了,然后当的代码就是flag没有标记的
                        ans[j]++;
                }
            }
        }
        for(i=0; i<=n-2; i++)
            printf("%d ",ans[i]);//输出格式
        printf("%d\n",ans[n-1]);
    }
    return 0;
}

  • 此题较难理解再来个代码看看
#include <bits/stdc++.h>
using namespace std;
int a[25],b[25];//没有标记的数组
int t,n,sum;
int main()
{
    while(cin>>t)
    {
        while(t--)
        {
            cin>>n;
            int zong=0;
            for(int i=0; i<n; i++)
            {
                cin>>a[i];
                zong+=a[i];
            }
            memset(b,0,sizeof(b));
            for(int i=0; i<(1<<n); i++)
            {
                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>zong/2)
                            if(sum-a[j]<=zong/2)
                                b[j]++;
            }
            for(int i=0; i<n; i++)
            {
                if(i)
                    printf(" %d",b[i]);
                else
                    printf("%d",b[i]);
            }
            printf("\n");
        }
    }
    return 0;

  • 不知道看没看懂,反正再来个(稳住,我们能赢)
    5.进阶题目趣味解题
    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

  • 详解在下一节哦~~(知道你看到这已经不容易了)
  • 下节提示
    acm新手小白必看系列之(5)——枚举进阶例题精讲
发布了8 篇原创文章 · 获赞 73 · 访问量 4775

猜你喜欢

转载自blog.csdn.net/qq_45899321/article/details/103824471
今日推荐