hihocoder第158周非法二进制数(dp)

xiaoxiao2021-02-28  142

描述

如果一个二进制数包含连续的两个1,我们就称这个二进制数是非法的。 小Hi想知道在所有 n 位二进制数(一共有2n个)中,非法二进制数有多少个。 例如对于 n = 3,有 011, 110, 111 三个非法二进制数。 由于结果可能很大,你只需要输出模109+7的余数。

输入

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

输出

n 位非法二进制数的数目模109+7的余数。

样例输入

3

样例输出

3

解题思路

如上图所示,可以将最终的结果的划分成两部分进行求解。 第一部分是红色三角号标识位置,经过观察可以发现红色部分由上一步红色三角号和蓝色三角号的共同标识部分即上一步的结果ans*2便可以得到,且其标识的即为已知的非法二进制数,初始值为1(即11这一二进制数); 第二部分是蓝色三角号标识的位置,由第一部分求解的思路推测便可知,第二部分标识的是除去第一部分已知的非法二进制数而在当前一步新增加的。新增加的只有一种满足的情况,即在上一步不是非法二进制的二进制数中末位为1,倒数第二位为0的情况(在图中用序号标识了上一步满足情况的二进制数),该部分可以总结出动态转移方程。具体见代码。

代码实现

#include <iostream> #include<cstdio> using namespace std; #define mod 1000000007 int dp[100][2]; int ans,n; void init() { dp[1][0]=1,dp[1][1]=1,dp[2][0]=2,dp[2][1]=1; for(int i=3;i<=n+2;i++) { dp[i][0]=(dp[i-1][0]%mod+dp[i-1][1]%mod)%mod; dp[i][1]=dp[i-1][0]%mod; } } int main() { scanf("%d",&n); init(); if(n==1) ans=0; else if(n==2) ans=1; else { ans=1; for(int i=3;i<=n;i++) { ans=(ans*2%mod+dp[i-2][0]%mod)%mod; } } printf("%d\n",ans); return 0; }

PS:

貌似发现了更简单的………..先求出所有非非法二进制数。上面也有提到其动态转移方程,即: dp[0][0]=1,dp[0][1]=1; dp[i][0]=(dp[i-1][0]%mod+dp[i-1][1]%mod)%mod; dp[i][1]=dp[i-1][0]%mod; total=(dp[n-1][1]+dp[n-1][0])%mod; 用 2n 2 n 直接减去这个结果就可以了,值得注意的是,有可能会发生ans小于total的情况,需要特判。

代码实现

#include <iostream> #include<cstdio> using namespace std; #define mod 1000000007 int dp[105][2]; int ans=0; int main() { int n; scanf("%d",&n); dp[0][0]=1,dp[0][1]=1; for(int i=1;i<n;i++) { dp[i][0]=(dp[i-1][0]%mod+dp[i-1][1]%mod)%mod; dp[i][1]=dp[i-1][0]%mod; } int total=(dp[n-1][1]+dp[n-1][0])%mod; int ans=1; for(int i=0;i<n;i++) ans=(ans*2)%mod; if(ans<total) ans+=mod; ans=ans-total; printf("%d\n",ans); return 0; }
转载请注明原文地址: https://www.6miu.com/read-37944.html

最新回复(0)