dp递推 zoj 3747 Attack on Titans

题意:

给n个士兵排队,每个士兵三种G、R、P可选,求至少有m个连续G士兵,最多有k个连续R士兵的排列的种数。

分析:

不是很好想的一道题。但是想通之后也是可以很好理解的。

我们试着将一个至多一个至少都变成至多的情况,至多K个连续的R,至少M个连续的G,这时我们可以看成是至多n个连续的G减去至多m-1个G,那剩下的情况不就是至少M个连续G的情况了吗。


dp[i][0] 表示的是当第i个士兵是R时的种数,此时至多u个连续G,至多v个连续R(u,v为常数);

dp[i][1] 表示的是当第i个士兵是G时的种数,此时至多u个连续G,至多v个连续R(u,v为常数);

dp[i][2] 表示的是当第i个士兵是P时的种数,此时至多u个连续G,至多v个连续R(u,v为常数)

最终的结果是dp[n][0]+dp[n][1]+dp[n][2] 。

递推关系为:

sum = dp[i-1][0] + dp[i-1][1] + dp[i-1][2] ;

当第i个士兵是P时没有任何限制,所以这时候dp[i][2] = sum 。

当第i个士兵是G的时候,当i<=u的时候,这时候没有超出限制,可以随便放,所以这时dp[i][1] = sum ; 

当i=u+1的时候,这时候要减去前u个士兵都是G的情况,所以这时dp[i][1] = sum -1 ; 

当i>u+1的时候,要减去i-u到i-1之间都是G的情况,这时i-1-u的位置可以放P或者R,所以dp[i][1] = sum - dp[i-u-1][0] - dp[i-u-1][2]。

当第i个士兵是R的时候,情况同上。


#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;

#define LL long long
#define mod 1000000007
int const maxn = 1100000;
LL dp[maxn][3];
//dp[i][0]表示的是第i个是R,dp[i][1]表示的是第i个是G,dp[i][2]表示的是第i个是P
LL u,v,n,m,k;

LL cal()
{
    //memset(dp,0,sizeof(dp));
    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][2] = sum ;
        //第i个是P的时候没有限制条件
        if(i<=u) dp[i][1] = sum ;
        else if(i==u+1) dp[i][1] = (sum - 1 +mod)%mod ;
        else dp[i][1] = (sum - dp[i-u-1][0] - dp[i-u-1][2] + mod)%mod;
        //第i个是G的时候,有限制条件,但i小于等于u的时候随意放,当i==u+1的时候要减去前u个都是G
        //的情况,当i>u+1的时候,要减去i-u到i-1之间都是G的情况,这时i-1-u的位置可以放P或者R
        if(i<=v) dp[i][0] = sum ;
        else if(i==v+1) dp[i][0] = (sum - 1 + mod)%mod ;
        else dp[i][0] = (sum - dp[i-v-1][1] - dp[i-v-1][2] +mod)%mod ;
        //同上

    }
    return (dp[n][0]+dp[n][1]+dp[n][2])%mod ;
}

int main()
{
    while(~scanf("%lld%lld%lld",&n,&m,&k))
    {
        u = n ;
        v = k ;
        //至多有u个连续的G,k个连续的R
        LL ans = cal();

        u = m-1 ;
        v = k ;
        ans = ((ans - cal())%mod + mod)%mod ;
        printf("%lld\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值