ZOJ 3747 Attack on Titans

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5170

题意:有n个位置,填充G,P,R三个字母,问填充至少m个连续的G,至多k个连续的R的情况有多少种

分析:动态规划,dp[i][j](j = 0->G,j=1->P,j = 2->R)

dp[i][j]表示第i个数填充j对应的字母的时候,前i个数有至多不超过u个连续的G和v个连续的R

j=1时,填充i不影响之前的结果,dp[i][1] = dp[i-1][0] + dp[i - 1][1] + dp[i - 1][2],设结果为sum

j=0时,当i位置<=m时,之前最多有m-1个连续的G,不影响结果dp[i][0] = sum

          i=m+1时,如果这m+1全为G就不符合题意,dp[i][0] = sum - 1

          i > m + 1时,它前面的m个字母如果全为G,则再前面的字母无论为什么情况都不符合题意,但是不能为G,因为如果是G的话,连续的就超过m个了.dp[i][0] = sum - dp[i - m -  1][1] - dp[i - m -  1][2]

k同理

至少m个,至多k个就是至多n个,至少k个-至多m-1个,至少k个

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll mod = 1e9 + 7;
const int maxn = 1e6 + 10;
int n,m,k;
ll dp[maxn][3];//dp[i][j]表示前i个位置放l至多u个连续的R和至多v个连续的G的方案个数
ll work(int u,int v)
{
    dp[0][0] = 1;
    dp[0][1] = 0;
    dp[0][2] = 0;
    for(int i = 1; i <= n; i++)
    {
        ll sum = (dp[i - 1][0] + dp[i - 1][1] + dp[i - 1][2]) % mod;
        dp[i][1] = sum;
        if(i <= u)//i-1一定小于u,所以不存在可能为u+1长度的情况
        {
            dp[i][0] = sum;
        }
        else if(i == u + 1)//第i个位置放G,i的前u个位置也放G
        {
            dp[i][0] = (sum - 1) % mod;
        }
        else//第i个位置放G,它前面有连续u个数全为G,再前面的是啥都行,后面的由前面推过来,而且再前面只能是R或者P
        {
            dp[i][0] = ((sum - dp[i - u - 1][1] - dp[i - u - 1][2]) % mod + mod) % mod;
        }
        if(i <= v)//更新R和更新G类似
        {
            dp[i][2] = sum;
        }
        else if(i == v + 1)
        {
            dp[i][2] = (sum - 1) % mod;
        }
        else
        {
            //注意减法取模写法
            dp[i][2] = ((sum - dp[i - v - 1][0] - dp[i - v - 1][1]) % mod + mod) % mod;
        }
    }
    return (dp[n][0] + dp[n][1] + dp[n][2]) % mod;
}
int main()
{
    //多组输入,memset超时
    while(cin>>n>>m>>k)//至多n个G,k个R-至多m - 1个G,k个R
    {
        //memset(dp,0,sizeof(dp));
        ll cnt1 = work(n,k);
         //memset(dp,0,sizeof(dp));
        ll cnt2 = work(m - 1,k);
        ll res = ((cnt1 - cnt2) % mod + mod) % mod;
         cout<<res<<endl;
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值