ZOJ 3747/uva 10328

本文详细解析了ZOJ3747题目的算法实现过程,通过动态规划方法解决字母排列计数问题,区分不同条件下排列数量的计算,并提供了完整的代码示例。

ZOJ 3747

题意:有n个字母(只含A、B、C),求至少有m个连续的A,最多有k个连续的C的排列种数。


输入:n,m,k
输出:排列的种数


题意非常的简单,但是。。。
我们考虑问题的另一种表达:至多k个连续C,至多n个连续A情况减去至多k个连续C,至多(m-1)个连续A 情况。


//dp[i][0]表示第i个为A,至多有u个连续A,至多有v个连续C的个数  //这里的u和v固定


//dp[i][1]表示第i个为B,至多有u个连续A,至多有v个连续C的个数  //这里的u和v固定


//dp[i][2]表示第i个为C,至多有u个连续A,至多有v个连续C的个数  //这里的u和v固定


当第i个为B时不会对A C的连续产生影响:
dp[i][2]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2];


当第i个为A时
i<u 绝对不会超过u个连续这个限制条件 
所以dp[i][0]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2];


如果i=u+1时,要排除前u个都放了G的情况,dp[i][0]=dp[i-1][0]+dp[i-1][1]+dp[i-1][2]-1;
这里为什么要单独分类讨论呢,因为i=u+1时,i-u-1等于0,出现bug.


如果i>u+1时,要排除从i-1到i-u位置都放了G的情况,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];


当第i个为C时,同上。



n=1,2,3......时如下图

当i=u+1时

比如:假设i=3,不能超过2个,如果要找两个连续G的情况,只要前两个不是两个G都可以!!!只有一种情况!!!

当i>u+1时

比如:假设i=4,不能超过2个,要找2到3位置都有G的情况,只有1位置可以放三种,所以由1位置决定。就找dp[4-2-1][1]和dp[4-2-1][2],即把连续断开!!!


#include<bits/stdc++.h>
#define  ll long long
#define  M 1000000007
using namespace std;
ll dp[1100000][5];
ll n,m,k,u,v;
ll solve()
{
	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])%M;
	
		dp[i][2]=sum;
		
		if(i<=u)
		dp[i][0]=sum;
		else if (i==u+1)
		dp[i][0]=(sum-1)%M;
		else 
		dp[i][0]=(sum-dp[i-u-1][1]-dp[i-u-1][2])%M;
		
		if(i<=v)
		dp[i][1]=sum;
		else if (i==v+1)
		dp[i][1]=(sum-1)%M;
		else 
		dp[i][1]=(sum-dp[i-v-1][0]-dp[i-v-1][2])%M;
	}
	return (dp[n][0]+dp[n][1]+dp[n][2])%M; 
}
int main()
{
	while(~scanf("%lld%lld%lld",&n,&m,&k))
	{
	u=n,v=k;
	ll ans=solve();
	u=m-1,v=k;
	ans=((ans-solve())%M+M)%M;
	printf("%lld\n",ans);  
	}
	return 0;
 } 

uva 10328 

显然n张牌总的排列数为2^n,如果减去最多有(k-1)张连续正面的情况,就是最后的结果。

dp[i][0]:表示第i张牌为正面,且最多有k张连续的正面的种数

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

dp[i][0] 当i<=k时,dp[i][0]=dp[i-1][0]+dp[i-1][1]

当i==k+1时,dp[i][0]=dp[i-1][0]+dp[i-1][1]-1;

当i>k+1时,dp[i][0]=dp[i-1][0]+dp[i-1][1]-dp[i-k-1][1];

思路同上题

详解
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值