【题解】zoj3747 线性DP

题目链接
思路摘抄自大佬博客
由于又是至多又是至少没办法处理,所以统一转换成至多,对于事件A={至多N个G士兵连续},事件B={至多m-1个G士兵连续},那么A-B={连续人数在m到n中取值,即至少m个连续士兵}。统一之后,设定一个二维数组,dp[i][3],dp[i][0]表示对于第i个位置,放G士兵,同理,dp[i][1],dp[i][2]分别表示第i个位置放R,和P。
最大连续u个G,连续v个R;
对于当前第i个位置,sum=dp[i-1][0]+dp[i-1][1]+dp[i-1][2];
由于放P对整个没有影响,所以,dp[i][2]=sum;
对于A事件
当i <= u 时,dp[i][0]=sum; //至多连续u个,那么小于等于u,就可以直接放G
当i==u+1时,dp[i][0]=sum-1; // 对于现在这个位置,之前可能出现连续u个的情况,当前要放G,就要减去那一种情况
当i>u+1时,dp[i][0]=sum-dp[i-u-1][1]-dp[i-u-1][2]; //此时要减去i前面已经出现连续u个G的情况,即从i-u到i-1这一段都是G,那么i-1-u的位置可以是P或者R, //于是减去dp[i-u-1][1],dp[i-1-u][2]合起来总共具有的情况数。 //对于B事件同理
当i<=v时,dp[i][1]=sum;
当i==v+1时,dp[i][1]=sum-1;
当i>v+1时,dp[i][2]=sum-dp[i-v-1][0]-dp[i-v-1][2];

#include<cstdio>
#include<iostream>
#define N 1000009
#define mod 1000000007
using namespace std;
typedef long long ll;
ll n,m,k;
ll dp[N][5];
//dp[i][0]表示对于i位置放G士兵
//dp[i][1]表示对于i位置放R士兵
//dp[i][2]表示对于i位置放P士兵
ll fun(ll u,ll v)
{
    //memset(dp,0,sizeof dp);
    dp[0][2]=1;                 //初始化时,对于第0个位置,整体为1即可,也可理解为对第0个位置放P,并没有什么影响。
    dp[0][0]=dp[0][1]=0;

    for(int i=1;i<=n;i++)
    {
        ll sum=((dp[i-1][0]+dp[i-1][1]+dp[i-1][2])%mod+mod)%mod;

        dp[i][2]=sum;

        if(i<=u) dp[i][0]=sum;
        if(i==u+1) dp[i][0]=((sum-1)%mod+mod)%mod;
        if(i>u+1)  dp[i][0]=((sum-dp[i-u-1][1]-dp[i-u-1][2])%mod+mod)%mod;

        if(i<=v) dp[i][1]=sum;
        if(i==v+1) dp[i][1]=((sum-1)%mod+mod)%mod;
        if(i>v+1)  dp[i][1]=((sum-dp[i-v-1][2]-dp[i-v-1][0])%mod+mod)%mod;
    }
    return (dp[n][0]+dp[n][1]+dp[n][2])%mod;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out1.txt","w",stdout);
    while(~scanf("%lld%lld%lld",&n,&m,&k))
    {
        ll ans;
        ans=fun(n,k);
        printf("%lld\n",((ans-fun(m-1,k))%mod+mod)%mod);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41958841/article/details/81635501