题意
有n个球要求你染成至少m个连续红色的方案
思路
dp[i]表示在i这个位置放红色球至少有m个红色球连续的方案数
当i<m时显然答案为0
当i==m时答案为1
当i>m时考虑:
1 dp[i-1]因为已经满足>=m所以肯定存在已经至少有m个连续的情况也就是说这时是dp[i]=dp[i-1]*2
2 从i这个位置之前的所有位置没有满足至少有m个连续,当加上i这个位置的红色球时才满足至少有m个球连续,那么这时候的方案为:[i-m+1,i]全红色,i-m为蓝色[1,i-m-1]所有不符合至少m个连续的方案都满足,就是2^(i-m-1)-dp[i-m-1]
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define mod 1000000007
const int MAXN=1e5+50;
typedef long long ll;
ll dp[MAXN];
ll qpow(ll a,ll b)
{
ll res=1;
while(b)
{
if(b&1) res=(res*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return res;
}//会爆int所以用longlong快速幂取模
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(dp,0,sizeof(dp));
dp[m]=1;
for(int i=m+1;i<=n;i++)
{
dp[i]=(dp[i-1]*2%mod+(qpow(1ll*2,1ll*(i-m-1))-dp[i-m-1])%mod+mod)%mod;
}
printf("%d\n",dp[n]);
}
return 0;
}
思路二
将至少的问题转化为至多的问题,题目求至少有m个红色的球连续,那么我们将问题转化成至多有n个红球连续的方案数-至多有m-1个红球连续的方案数,就是答案:至少有m个红球连续的方案数。假设u为求的至多有u个红球连续的方案数.
dp[i][0] 表示在i这个位置放红球的至多有u个红球连续的方案数
dp[i][1] 表示在i这个位置放蓝球的方案数
最后的答案就是(dp[n][0]+dp[n][1])-(dp[m-1][0]+dp[m-1][1])
至多放x个红球的方案数应该是dp[x][0]+dp[x][1],因为蓝球的方案数也算。
接下来考虑
当i<=u时,不管怎么放都不会达到超过至多u个红球的方案数,所以dp[i][0]=dp[i-1][0]+dp[i-1][1],因为对i这个位置放蓝球没什么影响所以dp[i][1]=dp[i-1][0]+dp[i-1][1]
当i==u+1时,应该舍弃前i-1个都是红球的情况,所以dp[i][0]=dp[i-1][0]+dp[i-1][1]-1
当i>u+1时,应该舍去[i-u,i-1]这个区间都是红色,i-u-1这个位置是蓝色的情况这种情况就是dp[i-u-1][1],所以dp[i][0]=dp[i-1][0]+dp[i-1][1]-dp[i-u-1][1]
注意:对于减法的取模操作应该要+mod在%mod
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
const int MAXN=1e5+50;
#define mod 1000000007
int n,m;
ll dp[MAXN][2];
ll solve(int u)
{
dp[0][0]=1;
dp[0][1]=0;
ll sum;
for(int i=1;i<=n;i++)
{
sum=(dp[i-1][0]+dp[i-1][1])%mod;
dp[i][1]=sum;
if(i<=u) dp[i][0]=sum;
else if(i==u+1) dp[i][0]=(sum-1)%mod;
else if(i>u+1) dp[i][0]=((sum-dp[i-u-1][1])%mod+mod)%mod;
}
return (dp[n][0]%mod+dp[n][1]%mod)%mod;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
printf("%lld\n",((solve(n)-solve(m-1))%mod+mod)%mod);
}
return 0;
}