Attack on Titans ZOJ - 3747

本文深入解析了一道关于Attack on Titans的DP算法题目,通过转换至少限制为至多限制的巧妙思路,解决了求解特定条件下士兵排列组合的问题。文章详细介绍了DP状态转移方程的推导过程,并提供了AC代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Attack on Titans

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3747

题目大意:

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

输入:

There are multiple test cases. For each case, there is a line containing 3 integers N (0 < N < 1000000), M (0 < M < 10000) and K (0 < K < 10000), separated by spaces.

输出:

One line for each case, you should output the number of ways mod 1000000007.

示例:

输入:3 2 2

输出:5

思路:

原谅我菜这题想了很久…不会DP, T-T想到了用DP[ i ] [ j ] 来表示,其中 i 表示排到了第几个士兵,j代表士兵的种类,但是这个至少连续m个G士兵的处理有点复杂,写了个n方复杂度算法TLE。想不出就去看了别的大佬的思路。
至少的限制条件可以转换为至多的表示。
至少m个连续G的所有排列数 = 至多n个连续的G排列数 - 至多m-1个连续的G排列数,这样连续的G的数目在 m~n之间。
然后就是DP的状态转移方程了。
我们用DP[ i ][ 1 ]表示第 i 个排 G ,DP[ i ] [ 2 ] 排 R,DP[ i ] [ 3 ] 排 P。

1.先考虑没有限制条件的 P士兵,显然P直接排在第 i 位就行,DP[ i ] [ 3 ] = DP [ i - 1] [ 1 ] +DP [ i - 1 ] [ 2 ] + DP [ i - 1 ] [ 3 ]

2.若连续的 G 至多为 u 个,则有三种情况:
i <= u 此时 G 如何排都不会连续超过 u 个,DP[ i ] [1 ] = DP [ i - 1] [ 1 ] +DP [ i - 1 ] [ 2 ] + DP [ i - 1 ] [ 3 ] 。
i = u + 1 : 排除前面已有连续 u 个 G 的一种情况
即:DP[ i ] [1 ] = DP [ i - 1] [ 1 ] +DP [ i - 1 ] [ 2 ] + DP [ i - 1 ] [ 3 ] - 1 。
i > u + 1 :则排除从i-1到i-u位置都放了G的情况,
DP [ i ] [ 1 ] = DP [ i - 1 ] [ 1 ] + DP[ i - 1 ] [ 2 ] + DP[ i - 1 ] [ 3 ] - DP[ i - u - 1 ] [ 2 ] - DP [ i - u - 1 ] [ 3 ];

3.同理也可写出DP [ i ] [ 2 ]的转移方程。
最后记得取模,下面见代码。

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mod 1000000007
#define MAXN 1000000
ll dp[MAXN][5];//1代表G,2代表R,3代表P 
int N,M,K;
ll solve(ll u,ll v){//至多u个连续的G,至多v个连续的R 
	dp[0][1]=1;
	dp[0][2]=dp[0][3]=0;
	for(int i=1;i<=N;++i){
		ll sum=((dp[i-1][1]+dp[i-1][2])%mod+dp[i-1][3])%mod;
		dp[i][3]=sum; 
		if(i<=u) dp[i][1]=sum;
		else if(i==u+1) dp[i][1]=(sum-1+mod)%mod;
		else if(i>u+1) dp[i][1]=(sum-(dp[i-u-1][2]+dp[i-u-1][3])%mod+mod)%mod;
		
		if(i<=v) dp[i][2]=sum;
		else if(i==v+1) dp[i][2]=(sum-1+mod)%mod;
		else if(i>v+1) dp[i][2]=(sum-(dp[i-v-1][1]+dp[i-v-1][3])%mod+mod)%mod;
	}
	
	return ((dp[N][1]+dp[N][2])%mod+dp[N][3])%mod;
}
int main()
{
	ios::sync_with_stdio(false);
	while(cin>>N>>M>>K){
		ll A,B;
		A=solve(N,K);
		B=solve(M-1,K);
		cout<<(A-B+mod)%mod<<endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值