大一寒假培训(四)——二进制枚举

大一寒假培训(四)

今天学习的知识是二进制枚举
首先,了解一下二进制操作

二进制操作

算数位运算(与、或、非、异或)
与(&)

对于指定的两个数 A=60(0011 1100)
B=13(0000 1101)
执行一下操作 A&B=12(0000 1100)
就是对二进制每一位进行了一次与操作,同为1,结果为1,否则为0

或(|)

对于指定的两个数A=60(0011 1100)
B=13(0000 1101)
执行一下操作 A|B=61(0011 1101)
就是对二进制每一位进行了一次或操作,同为0,结果为0,否则为1

非(~)

对于指定的一个数 A=60(0011 1100)
执行以下操作 ~A=195(1100 0011)
就是对二进制每一位进行了一次取反操作,若二进制数位0,则变成1,否则变成0
此时是只考虑了一个字节(8 bite)

异或

异或,英文为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)
就是对二进制每一位进行了一次异或操作,即非进位加法。

二进制移位操作符

移位操作有两种左移与右移:
1、左移<<
例如:A=5(0101)
如果向左移动一位即A<<1结果为1010,十进制的10。
二进制中的左移就是乘二操作,在c/c++中左移运算速度比乘二速度要快。
2、右移>>
例如:A=5(0101)
如果向右移动一位即A>>1结果为0010,十进制的2。
二进制中的左移就是除二操作(舍去小数)。

二进制枚举

二进制枚举利用的是二进制下n位长度的数有2n个,一个有n个元素的集合子集个数也为2n
所以可以利用二进制的1,0和集合中的元素联系起来
他可以实现组合也可以实现容斥
对一个二进制来说1代表取这个元素0代表不取这个元素,1和0所在的位置代表元素的位置
这样的思想在有时候给题目有了很大的方便
举个例子
如集合{a,b,c,d,e}当二进制00000就代表什么都不取, 10000代表取a,01000代表取b,11000代表取a,b
如此所以我们需要枚举的数量就是00000到11111,也就是0到1<<n位,<<代表左移操作

nefu 1172 Find different

题意:输入奇数个数,其中只有一个数不是两两配对的,请把它找出来(为什么要把这个单身狗找出来呢)

这道题的原理上面讲过了:对于一个值异或同一个值两次,则结果还是原值
所以这道题只需要把所输入的数据一直异或,单身狗自动就跑出来了

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n,ans,x;
    while(cin>>n){
        ans=0;
        for(int i=0;i<n;i++){
            cin>>x;
            ans=ans^x;	//异或同一个值两次,等于没有变化
        }
        cout << ans << endl;
    }
    return 0;
}

nefu 1205 和为K

Description
给出长度为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 <bits/stdc++.h>
using namespace std;
int num[25];
int main()
{
    int n,k,ans,flag;
    while(cin>>n>>k){
        flag=1;
        for(int i=0;i<n;i++)
            cin >> num[i];
        for(int i=0;i<(1<<n);i++){	//已知有n个数,故有2^n^种情况,这里把这些情况遍历一遍
            ans=0;
            for(int j=0;j<n;j++)	//j的作用是判断i在二进制种的每一位上是0还是1
                if(i&(1<<j))		//1<<j在二进制下为1后面j个0(从后往前第j+1位为1),条件即为i从后往前的第j+1位与1进行与运算且结果不为0
                    ans+=num[j];	//若i的那一位为0,则结果为0,否则结果不为0
            if(ans==k){
                cout << "Yes" << endl;
                flag=0;
                break;
            }
        }
        if(flag==1)
            cout << "No" << endl;
    }
    return 0;
}

nefu 1505 陈老师加油

Description
陈老师经常开车在哈尔滨的大街上行走,假设刚开始油箱里有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,lu,jia,dai,ans;
    while(cin>>t){
        ans=0;
        for(int i=0;i<(1<<15);i++){
            lu=jia=0;dai=t;
            for(int j=0;j<15;j++){  //0为加油站,1为路口
                if(i&(1<<j)){       //如果不为0,则i的那一位为1,即路口
                    dai--;
                    lu++;
                }
                else{				//此时为0,即加油站
                    dai*=2;
                    jia++;
                }
                if(dai<=0)			//如果油没了,就直接退出循环
                        break;
            }
            if(lu==10&&jia==5&&dai==0)	//保证最后的油量为0
                ans++;
        }
        cout << ans << endl;
    }
    return 0;
}

nefu 1518 纸牌游戏

Description
给你一些扑克,每张都对应一个点数,分别对应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

跟和为K那道题很像,只是这次是要求方案数

#include <bits/stdc++.h>
using namespace std;
int a[25];
int main()
{
    int n,p,sum,ans;
    while(cin>>n>>p){
        for(int i=0;i<n;i++)
            cin>>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)		//总和是p时,将方案数加1
                ans++;
        }
        cout << ans << endl;
    }
    return 0;
}

nefu 1641 权利指数

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 <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))&&sum>zong/2&&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;
}

nefu 1285 趣味解题

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 <bits/stdc++.h>
using namespace std;
double a[15],b[15],c[15],ac[15],wa[15];
int main()
{
	ios::sync_with_stdio(0);
    int t,n,x,ans;
    double gai,sum;
    while(cin>>t){
        while(t--){
            cin>>n;
            for(int i=0;i<n;i++)
                cin>>a[i];
            for(int i=0;i<n;i++)
                cin>>b[i];
            for(int i=0;i<n;i++)
                cin>>c[i];
            cin>>x;
            for(int i=0;i<n;i++){
                wa[i]=(1-a[i])*(1-b[i])*(1-c[i]);	//都做不出来的时候算没做出来
                ac[i]=1-wa[i];
            }
            sum=0;
            for(int i=0;i<(1<<n);i++){
                gai=1;ans=0;
                for(int j=0;j<n;j++){
                    if(i&(1<<j)){   //做出来的
                        gai=gai*ac[j];
                        ans++;
                    }				//没做出来的
                    else
                        gai=gai*wa[j];
                }
                if(ans==x)
                    sum=sum+gai;
            }
            printf("%.4lf\n",sum);
        }
    }
    return 0;
}
要求: 1、写出设计思路、算法思路。 2、写出程序。 3、运行结果截图。 第1题、两倍 给定2到15个不同的正整数,你的任务是计算这些数里面有多少个数对满足:数对中一个 数是另一个数的两倍。比如给定 1 4 3 2 9 7 18 22 得到的答案是3,因为2是1的两倍,4是2的两倍,18是9的两倍。 第2题、肿瘤面积 在一个正方形的灰度图片上,肿瘤是一块矩形的区域,肿瘤的边缘所在的像素点在图片 中用0表示,其他肿瘤内和肿瘤外的点都用255表示。编写一个程序,计算肿瘤内部的像 素的点的个数(不包括肿瘤边缘上的点)。已知肿瘤的边缘平行于图像的边缘。图像数 据中第一行为图像像素的行数和列数,随后为像素数据。比如,图像数据为 7 14 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 0 0 0 0 0 0 0 0 255 255 255 255 255 255 0 255 255 255 255 255 255 0 255 255 255 255 255 255 0 255 255 255 255 255 255 0 255 255 255 255 255 255 0 255 255 255 255 255 255 0 255 255 255 255 255 255 0 0 0 0 0 0 0 0 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 结果为18。 第3题、FBI树 二进制串只能由"0"和"1"组成。将由"0"和"1"组成的字符串分为三类:全"0"串称为B串 ,全"1"串称为I串,既含"0"又含"1"的串则称为F串。 二进制串可以转换为FBI树结构,FBI树是一棵二叉树,在该二叉树中包含F节点、B节点 和I节点三种。 可以将一个长度为2n的二进制串S构造为一棵FBI树T,方法为: T的根结点为R,其类型与串S的类型相同; 若串S的长度大于1,将串S从中间分开,分为等长的左右子串S1和S2;由左子串S1构造R 的左子树T1,由右子串S2构造R的右子树T2。 现在给定一个长度为2n的二进制串,请用上述构造方法构造出一棵FBI树,并输出它的后 序遍历序列。 输入数据有2行,第一行是一个整数N(0<=N<=10),第二行是一个长度为2N的二进制串。 如输入数据为 3 11011000 输出为 IIIBIFFIBFBBBFF 第4题(http://poj.org/problem?id=1050) To the Max Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 25250 Accepted: 13051 Description Given a two-dimensional array of positive and negative integers, a sub- rectangle is any contiguous sub-array of size 1*1 or greater located within the whole array. The sum of a rectangle is the sum of all the elements in that rectangle. In this problem the sub-rectangle with the largest sum is referred to as the maximal sub-rectangle. As an example, the maximal sub-rectangle of the array: 0 -2 -7 0 9 2 -6 2 -4 1 -4 1 -1 8 0 –2 is in the lower left corner: 9 2 -4 1 -1 8 and has a sum of 15. Input The input consists of an N * N array of integers. The input begins with a single positive integer N on a line by itself, indicating the size of the square two-dimensional array. This is followed by N^2 integers separated by whitespace (spaces and newlines). These are the N^2 integers of the ar
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值