(zoj3747) Attack on Titans

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/AC_jie/article/details/79944870

今天开始做DP,今天的这个DP是带有限制条件的,并且和组合数学有关。
首先这个题第一个突破点是将至少条件转化为至多的条件,这个很重要,决定了解题的方向。因为在规划问题状态的时候很难对至少进行处理,并且不易进行状态的划分。

这样的话,将问题就转化成了,至多连续n个G,至多连续k个R 减去 至多连续 m-1 个G,至多连续K个R,现在问题的状态就转换成了求至多上面两个状态的方法数。

然后考虑每新加入一个士兵,会对原来的产生那些影响,因为加入的士兵分为三类,所以将状态划分为,dp[i][3]。
dp[i][0] 代表 当第i位置是G士兵时的方法数。
dp[i][1] 代表 当第i位置是R士兵时的方法数。
dp[i][2] 代表 当第i位置是P士兵时的方法数。

当加入的是P士兵时,dp[i][2] = dp[i-1][0] +dp[i-1][1] + dp[i-1][2]
当加入的是G士兵时,因为有至多连续这个条件的限制,需要分情况讨论下

假设m为限制条件,sum = dp[i - 1][0] + dp[i-1][1] + dp[i-2][2]

1/当加入的数量小于m时,dp[i][1] = sum
2/当加入的数量=m + 1 时,dp[i][1] = sum - 1; 为什么要减一,因为限制条件的限制,当加入的这个士兵为G时,它前面肯定存在从1号位置到m号位置的情况全为G的情况,这时再加入G的话,就不满足条件了,还有因为设计状态的时候是,确定第i位置为G的,也就是说,在这种状态下,要去除从 1~m 全为G的情况,来满足当前的状态。
3/当加入的士兵的位置大于m + 1 时,因为当前位置为G,所以出现从位置i-m - 1 到 i-1 都为G的话,在加入G的话,就会不满足条件,然后需要将这种情况包含的方法数减去。
就是dp[i][1] = sum - dp[i - m - 1][0] - dp[i - m -1][2] 的方法数,就是减去1到 i - m - 1 这个范围内可能出现的情况数。就是减去不满足的那些情况数。

当加入士兵R时,同上。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    const int MAXN = 2e6 + 10;
    const int P = 1e9 + 7;
    typedef long long LL;
    LL dp[MAXN][3];
    int n;
    LL f(int m,int k)
    {
        LL sum = 0;
        dp[0][0] = 1;//?
        dp[0][1] = dp[0][2] = 0;
        for(int i = 1;i <= n;++i)
            {
                sum = (dp[i - 1][0]  + dp[i - 1][1]  + dp[i - 1][2] ) % P;
                dp[i][2] = sum;
                if(i <= m) dp[i][0] = sum;
                if(i == m + 1) dp[i][0] = (sum - 1 + P) % P;
                if(i > m + 1) dp[i][0] = (sum - dp[i - m - 1][1] - dp[i - m - 1][2] + P) % P;

                if(i <= k) dp[i][1] = sum;
                if(i == k + 1) dp[i][1] = (sum - 1 + P) % P;
                if(i > k + 1) dp[i][1] = (sum - dp[i - k - 1][0] - dp[i - k - 1][2] + P) % P;
            }
        LL ans = (dp[n][0] + dp[n][1] + dp[n][2]) % P;
        return ans;
    }
    int main()
    {
        ios::sync_with_stdio(false);
        int m,k;
        while(~scanf("%d%d%d",&n,&m,&k))
        {
            LL ans = 0;
           // memset(dp,0,sizeof(dp));
            ans = ((f(n,k) - f(m - 1,k)) % P + P) % P;
            cout << ans << endl;
        }
        return 0;

    }

这个题的技巧,和坑点在于,减法的取模运算注意“补偿“。
还有这个题的难点在于组合计数,应用的还是递推的思想,注意”分析每加入一个时,产生的影响,以及状态的设计。

猜你喜欢

转载自blog.csdn.net/AC_jie/article/details/79944870