算法---二进制/二进制枚举

#一 二进制操作
算数位运算:
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)
就是对二进制每一位进行了一次取反操作,若二进制数位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)
就是对二进制每一位进行了一次异或操作,即非进位加法。
例如:
(1)给出一个奇数n,(1 <= n <= 10000001)
给定一个具有n个数字的数组:a [1],a [2] a [3] … a [n]。它们是正数。
n / 2个数字出现两次,只有一个数字出现一次。
现在,您应该告诉我仅出现一次的号码。
有几个测试案例以EOF结尾。
第一行是整数n。
然后第二行有n个整数a [1],a [2] … a [n]。
对于每种情况,输出仅出现一次的数字。
样本输入
7
3 2 7 2 1 7 3
1
7
11
1 1 2 2 3 3 4 4 5 5 9
样本输出
1
7
9
代码如下:

`#include <bits/stdc++.h>
using namespace std;
int main()
{
    int a,b,ans,n,i;
    while(scanf("%d",&n)!=-1)
    {
        ans=0;
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a);
            ans=ans^a;
        }
        printf("%d\n",ans);
    }
    return 0;
}`

之后再看这个:

二进制移位操作符:
移位操作有两种左移与右移:
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位,<<代表左移操作
例如:
给出长度为n的数组,求能否从中选出若干个,使他们的和为K.如果可以,输出:Yes,否则输出No
输入:
第一行:输入N,K,为数组的长度和需要判断的和(2<=N<=20,1<=K<=10^9)
第二行:N个值,表示数组中元素的值(1<=a[i]<=10^6)
输出:
输出Yes或No
样例输入:
5 13
2 4 6 8 10
样例输出:
No
代码如下:

#include <bits/stdc++.h>
using namespace std;
int a[10000],f;
long long sum,i,j,n,k;
int main()
{
    sum=0;
    while(scanf("%d%lld",&n,&k)!=-1)
    {
        f=0;
        for(i=0; i<n; i++) scanf("%d",&a[i]);
        for(i=0; i<(1<<n); i++)//将所有可能性一一枚举。
        {
            sum=0;//这一步很重要。
            for(j=0; j<n; j++)
                if(i&(1<<j))//如果括号里为真值(不为0)即从数组选取一定数量的数相加;可以将所有情况列举;
                    sum=sum+a[j];
            if(sum==k)//判断是否符合条件。
            {
                printf("Yes\n");
                f=1;//标记。
                break;//找到之后立即退出。
            }
        }
        if(f==0)
            printf("No\n");
    }
    return 0;
}

觉得有用就点个赞叭。

发布了2 篇原创文章 · 获赞 2 · 访问量 28

猜你喜欢

转载自blog.csdn.net/qq_46137221/article/details/104112567