牛客练习赛35 - B背单词 - 3维的dp

题目描述

winterzz1准备考4级了,现在winterzz1决定把世界上所有单词都背一遍,winterzz1发现任意一个单词最多有A个连续的元音,最多有B个连续的辅音。且单词最长长度为N,winterzz1问你在满打满算的情况他需要背多少单词???

输入描述:

首先输入一个T(T<=100),表示有T组案例,每组案例依次输入三个正整数N,A,B,N<=5000,A<=50,B<=50;

输出描述:

输出winterzz1最多需要背多少单词,结果mod(10^9+7)

示例1

输入

复制

2
2 2 2
500 20 30

输出

复制

702
175540856

备注:

元音字母为a,e,i,o,u,其余21个字母均为辅音

思路:

开一个dp[5005][2][55]的数组

dp[i][j][k]:=判断到第i位,第i位的元素是j(j==0表示元音,j==1表示辅音),重复了k次有几个单词。

eg、dp[1][0][1]=5。第1位是元音有5种;dp[1][1][1]=21。第1位是辅音有21种

dp[2][0][2]=25。第2位是元音,且第1位也是元音有25种。

递推方程:

for(int j=1;j<=min(b,i-1);j++)dp[i][0][1]=(dp[i][0][1]+dp[i-1][1][j]*5)%mod;

第i位是元音,且第i-1位不是元音的情况,也就是说第i-1位是辅音的所有情况之和*5

for(int j=1;j<=min(a,i-1);j++)dp[i][1][1]=(dp[i][1][1]+dp[i-1][0][j]*21)%mod;

第i位是辅音,且第i-1位不是辅音的情况,也就是说第i-1位是元音音的所有情况之和*21

那么我们知道动态规划是由已知到未知的,怎么赋初值呢?

对于每个i,我们可以求出从元音重复个数从2到a的情况,和辅音重复个数为从2到b的情况,然后用递推方程求出在i位置上之重复1次的情况。

for(int j=2;j<=min(a,i);j++){//初始化,到第i个位置时连续2个元音及以上的情况
    dp[i][0][j]=dp[i-1][0][j-1]*5%mod;
}
for(int j=2;j<=min(b,i);j++){//初始化,到第i个位置时连续2个辅音及以上的情况
    dp[i][1][j]=dp[i-1][1][j-1]*21%mod;
}

(dp小白只能这样理解了qwq,每次推的时候都懵的一批,不知道怎么赋初值,不会推qwq)

代码如下:

#include<iostream>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#include<stack>
#include<sstream>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

const int N=5005,mod=1e9+7;
ll dp[N][2][55];
int main(){
    int t,a,b,n;
    scanf("%d",&t);
    while(t--){
        memset(dp,0,sizeof(dp));
        scanf("%d%d%d",&n,&a,&b);
        dp[1][0][1]=5;dp[1][1][1]=21;
        for(int i=2;i<=n;i++){
            for(int j=2;j<=min(a,i);j++){
                dp[i][0][j]=dp[i-1][0][j-1]*5%mod;
            }
            for(int j=2;j<=min(b,i);j++){
                dp[i][1][j]=dp[i-1][1][j-1]*21%mod;
            }
            for(int j=1;j<=min(b,i-1);j++)dp[i][0][1]=(dp[i][0][1]+dp[i-1][1][j]*5)%mod;
            for(int j=1;j<=min(a,i-1);j++)dp[i][1][1]=(dp[i][1][1]+dp[i-1][0][j]*21)%mod;
        }
        ll ans=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=a;j++){
                ans=(ans+dp[i][0][j])%mod;
            }
            for(int j=1;j<=b;j++){
                ans=(ans+dp[i][1][j])%mod;
            }
        }
        printf("%lld\n",ans%mod);
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值