蓝桥杯题目---非法二进制数

17 篇文章 0 订阅
15 篇文章 0 订阅
描述

如果一个二进制数包含连续的两个1,我们就称这个二进制数是非法的。

小Hi想知道在所有 n 位二进制数(一共有2n个)中,非法二进制数有多少个。

例如对于 n = 3,有 011, 110, 111 三个非法二进制数。

由于结果可能很大,你只需要输出模109+7的余数。
输入

一个整数 n (1 ≤ n ≤ 100)。
输出

n 位非法二进制数的数目模10^9+7的余数。
样例输入

    3

样例输出

    3

 思路:

像这一类计数问题一定要记住不遗漏,不重复。

这道题用动态规划计数,用d(i)表示i位非法二进制数的个数,i可以填0和1则分类,d0(i)表示以0结尾的非法数:

.分类计数加法原理:d(i)=d0(i)+d1(1);

1.  i填0的时候:d0(i)=d(i-1); //因为0不能为前面做出贡献,填0就等于d(i-1)

2. i填1的时候,

   (1)从不遗漏角度:首先 d1(i)肯定包含了d(i-1),d(i-1)就是看有没有遗漏,比如一个i-1位的数不是非法数但第i位填1就让它变成了非法数,比如1001填1后变成10011,所以这就是d(i-1)遗漏的补分,所以d1(i)就包含了d(i-1)和{i-1位以1为结尾的所有二进制数}

   (2)从不重复角度:U1= d(i-1) 和U2={i-1位以1为结尾的所有二进制数}这两个集合 重复部分U1U2=d1(i-1),  

  综上i填1的时候,d1(i)= U1 + U2 - d1(i-1) = d0(i-1) + U2 = d0(i-1) + 2^(i-2);



递推式:

i>2时

d[0][i]=d[0][i-1]+d[1][i-1];

d[1][i]=d[0][i-1]+pow(2,i-2);

i=1时 d[0][1]=d[1][1]=0;

i=2时 d[0][2]=0;d[1][2]=1;

最终结果是 d[0][n]+d[1][n];

代码:

#include <vector>
#include <stack>
#include <string>
#include <cstring>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <queue>
#define _for(i,a,b) for(int i=a;i<b;i++)
#define _unfor(i,a,b) for(int i=a;i>=b;i--)
#define RI(a) scanf("%d",&a)
#define mset(a,val,n) for(int i=0;i<n;i++)a[i]=val;
#define mset2(a,val,n,m) for(int i=0;i<n;i++)for(int j=0;j<m;j++)a[i][j]=val;
#define FI freopen("in.txt","r",stdin)
#define FO freopen("out.txt","w",stdout)
using namespace std;
typedef long long LL;

LL d[2][111],mod=1000000007,pow2[111];
int main() {
    int n;
    cin>>n;
    pow2[0]=d[1][2]=1;
    _for(i,1,n)pow2[i]=(pow2[i-1]*2)%mod;
    _for(i,3,n+1){
        d[0][i]=(d[0][i-1]+d[1][i-1])%mod;
        d[1][i]=(d[0][i-1]+pow2[i-2])%mod;
    }
    cout<<(d[0][n]+d[1][n])%mod<<endl;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值