描述
如果一个二进制数包含连续的两个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;
}