ZOJ - 3747 Attack on Titans [有条件限制的经典递推计数好题]
2018-02-27 11:20
441 查看
题目大意是 有n个士兵的排列,士兵有GRP三种类型,要求至少有m个G士兵连续,至多有k个R士兵连续,问有多少种方案,答案 取模1e9+7
题目分析
我们看到这个题目,很显然是一个计数的问题,那么如果从简单入手考虑,假设不存在上述条件,就是有n个士兵,每个士兵有三种类型,很显然就是3n3n种情况,然后现在我们加上假设条件,根据尝试,我们发现至多有K个R士兵连续这种情况比较容易考虑,这是我们可以采用递推的思路,现在我用f(n)来表示到达第n个位置时满足上述限制条件的种类数,很显然,当n<=K时,限制条件不起作用,所以 f(n)=f(n−1)∗3f(n)=f(n−1)∗3,当n=k+1时,只有一种情况:所有士兵全为R的时候的,是不满足条件的,此时我们只要删去这一种情况就可以了,即f(n)=f(n−1)∗3−1f(n)=f(n−1)∗3−1。
当n逐渐增加的时候,由于前面已经不存在不满足条件的情况了,所以,我们只要考虑最后一个新增的为R时,是否会形成连续k+1个R,假设形成了,有多少种这样的情况呢,很显然就是 f(n−k−1)f(n−k−1)种啦,即,当n>k+1n>k+1时,f(n)=f(n−1)∗3−f(n−k−1)f(n)=f(n−1)∗3−f(n−k−1)
但是我们现在有两个限制条件,并且至少有m个连续并不是很容易计算,此时,我们就会想到正难则反,我们通过计算至多有n个连续的G减去至多有m-1个连续的G的情况(连续人数的范围就控制在m-n之间)就会是我们要的至少有m个G连续。
再根据上面的方法 就能计算G的可能性。
由于现在我们有两个限制条件,三种士兵,所以我们不再简单的乘三,把各种情况都分离开来。用一个dp数组来记录种类数,dp[i][j] ,i表示当前序列种的第i个士兵,j代表士兵的种类。
0表示G,1表示P,2表示R;
现在我们的问题变成了 {至多n个连续的G,k个连续的R的个数} −−{至多m-1个连续的G,k个连续的R的个数}
那么怎么计算 至多u个连续G,v个连续R的种类数呢?
当i≤ui≤u时 显然 dp[i][0]=(dp[i-1][0]+dp[i-1][1]+dp[i-1][2])%mod;
而当i=u+1i=u+1时, dp[i][0]=(dp[i-1][0]+dp[i-1][1]+dp[i-1][2]-1)%mod;
i>u+1i>u+1时
dp[i][0]=((dp[i-1][0]+dp[i-1][1]+dp[i-1][2]-dp[i-u-1][1]-dp[i-u-1][2])%mod+mod)%mod;
//因为在我们删去的情况中i-u到i-1这一段都成了G,所以第i-u-1个不可能G,所以我们只要减去第i-u-1个为P,R的两种情况就好了。
以此递推即可解决,对于dp数组的初始化,我们只要满足一开始能够计算出三个位置的值,所以在第0个无意义的位置, 给其中一个赋初值为1就好了
代码如下
题目分析
我们看到这个题目,很显然是一个计数的问题,那么如果从简单入手考虑,假设不存在上述条件,就是有n个士兵,每个士兵有三种类型,很显然就是3n3n种情况,然后现在我们加上假设条件,根据尝试,我们发现至多有K个R士兵连续这种情况比较容易考虑,这是我们可以采用递推的思路,现在我用f(n)来表示到达第n个位置时满足上述限制条件的种类数,很显然,当n<=K时,限制条件不起作用,所以 f(n)=f(n−1)∗3f(n)=f(n−1)∗3,当n=k+1时,只有一种情况:所有士兵全为R的时候的,是不满足条件的,此时我们只要删去这一种情况就可以了,即f(n)=f(n−1)∗3−1f(n)=f(n−1)∗3−1。
当n逐渐增加的时候,由于前面已经不存在不满足条件的情况了,所以,我们只要考虑最后一个新增的为R时,是否会形成连续k+1个R,假设形成了,有多少种这样的情况呢,很显然就是 f(n−k−1)f(n−k−1)种啦,即,当n>k+1n>k+1时,f(n)=f(n−1)∗3−f(n−k−1)f(n)=f(n−1)∗3−f(n−k−1)
但是我们现在有两个限制条件,并且至少有m个连续并不是很容易计算,此时,我们就会想到正难则反,我们通过计算至多有n个连续的G减去至多有m-1个连续的G的情况(连续人数的范围就控制在m-n之间)就会是我们要的至少有m个G连续。
再根据上面的方法 就能计算G的可能性。
由于现在我们有两个限制条件,三种士兵,所以我们不再简单的乘三,把各种情况都分离开来。用一个dp数组来记录种类数,dp[i][j] ,i表示当前序列种的第i个士兵,j代表士兵的种类。
0表示G,1表示P,2表示R;
现在我们的问题变成了 {至多n个连续的G,k个连续的R的个数} −−{至多m-1个连续的G,k个连续的R的个数}
那么怎么计算 至多u个连续G,v个连续R的种类数呢?
当i≤ui≤u时 显然 dp[i][0]=(dp[i-1][0]+dp[i-1][1]+dp[i-1][2])%mod;
而当i=u+1i=u+1时, dp[i][0]=(dp[i-1][0]+dp[i-1][1]+dp[i-1][2]-1)%mod;
i>u+1i>u+1时
dp[i][0]=((dp[i-1][0]+dp[i-1][1]+dp[i-1][2]-dp[i-u-1][1]-dp[i-u-1][2])%mod+mod)%mod;
//因为在我们删去的情况中i-u到i-1这一段都成了G,所以第i-u-1个不可能G,所以我们只要减去第i-u-1个为P,R的两种情况就好了。
以此递推即可解决,对于dp数组的初始化,我们只要满足一开始能够计算出三个位置的值,所以在第0个无意义的位置, 给其中一个赋初值为1就好了
代码如下
#include <bits/stdc++.h> #define cl(arr,val) memset(arr,val,sizeof(arr)) using namespace std; typedef long long ll; const int mod=1e9+7; ll dp[1000005][4]; int n,m,k; ll solve(int u,int v) { //cl(dp,0); dp[0][2]=dp[0][0]=0; dp[0][1]=1; for(int i=1; i<=n; i++) { dp[i][1]=(dp[i-1][0]+dp[i-1][1]+dp[i-1][2])%mod; if(i<=u) { dp[i][0]=(dp[i-1][0]+dp[i-1][1]+dp[i-1][2])%mod; } else if(i==u+1) { dp[i][0]=(dp[i-1][0]+dp[i-1][1]+dp[i-1][2]-1)%mod; } else { dp[i][0]=((dp[i-1][0]+dp[i-1][1]+dp[i-1][2]-dp[i-u-1][1]-dp[i-u-1][2])%mod+mod)%mod; } if(i<=v) { dp[i][2]=(dp[i-1][0]+dp[i-1][1]+dp[i-1][2])%mod; } else if(i==v+1) { dp[i][2]=(dp[i-1][0]+dp[i-1][1]+dp[i-1][2]-1)%mod; } else { dp[i][2]=((dp[i-1][0]+dp[i-1][1]+dp[i-1][2]-dp[i-v-1][0]-dp[i-v-1][1])%mod+mod)%mod; } } return (dp [0]+dp [1]+dp [2])%mod; } int main() { while(scanf("%d%d%d",&n,&m,&k)!=EOF) { printf("%lld\n",(solve(n,k)-solve(m-1,k)%mod+mod)%mod); } return 0; }
相关文章推荐
- zoj 3747 Attack on Titans 带限制条件的计数递推dp
- zoj 3747 Attack on Titans 带限制条件的计数递推dp
- [ZOJ 3747] Attack on Titans (计数DP + 连续至多 + 连续至少)
- Attack on Titans ZOJ - 3747
- zoj 3747 Attack on Titans【递推好题】
- ZOJ 3747 Attack on Titans 2018-1-30
- Attack on Titans ZOJ - 3747 DP
- dp递推 zoj 3747 Attack on Titans
- zoj 3747 Attack on Titans dp 待补充
- ZOJ 3747 Attack on Titans
- Attack on Titans ZOJ - 3747 dp
- xtu 1233 Coins && zoj 3747 Attack on Titans
- ZOJ 3747 Attack on Titans
- ZOJ 3747 Attack on Titans
- ZOJ 题目3747 Attack on Titans(DP)
- [递推dp] zoj 3747 Attack on Titans
- ZOJ 3747 Attack on Titans
- zoj 3747 Attack on Titans 递推 计数dp
- ZOJ 3747 Attack on Titans(DP)
- 【zoj】【Attack on Titans】