Attack on Titans ZOJ - 3747(限制条件下的DP递推)

本文介绍了一道名为Attack on Titans的编程题解法,该题要求在无限数量的三种士兵(G、R、P)中选择N个,使得G士兵至少有M个连续出现,同时R士兵至多K个连续出现。通过将问题转化为计算特定条件下放置士兵的方案数,利用动态规划算法求解。

Attack on Titans

题目链接:ZOJ - 3747
题意:选出N个士兵编号1~N, 一共三种士兵, G,R,P三种, 士兵总数是无限大;G士兵至少有M个连续地, R士兵至多有K个连续地, P士兵随便放没有限制;
现在求这么一个事件Q:至少M个连续地士兵G&&至多K个连续的士兵R;
可以看出两个限制条件是不统一的, 一个至多, 一个至少,现在我们将他的限制条件统一;
设事件A为:至多N个连续地士兵G&&至多K个连续地士兵R;
设事件B为:至多M-1个连续地士兵G&&至多K个连续地士兵R;
那么可知Q=A-B;所以我们只需分别求出A,B的数量即可间接地求出Q;
A, B是相通的, 均为至多X个连续地士兵G&&至多Y个连续地士兵R;求解方式是相同的;
数组dp[i][j]表示第i个位置放的是j士兵(j==0:G;     j==1:R;     j==2:P;);
sum=dp[i-1][0]+dp[i-1][1]+dp[i-1][2];(sum表示前i-1个位置有几种情况);
对于P士兵, 由于是随便放, 没有限制条件, 所以在i位置可以放P而不需要考虑非法情况;
所以 dp[i][2]=sum;
对于G士兵,:
    当i <= X: 此时前边最多X-1个G, 第i个位置可以放G, 且不会有其他非法情况;
                    dp[i][0]=sum;
    当i == X+1: 只有当1~i-1全为G, 即G有连续X个时, i位置不能放G, 其他情况均可;
                    dp[i][0]=sum-1;

    当i > X+1: 当i-X~i-1全为G, 既有连续X个G, 位置不能放G, 此时i-X-1位置一定为P或R
                    dp[i][0]=sum-dp[i-X-1][1]-dp[i-X-1][2];


现在G士兵已放完, 下面放R士兵, 其实道理与G相同, 把X换成Y;
对于R士兵:
    当i <= Y: 此时前边最多Y-1个R, 第i个位置可以放R, 且不会有其他非法情况;
                    dp[i][1]=sum;
    当i == Y+1: 只有当1~i-1全为R, 即R有连续Y个时, i位置不能放R, 其他情况均可;
                    dp[i][1]=sum-1;

    当i > Y+1: 当i-Y~i-1全为R, 既有连续Y个R, 位置不能放R, 此时i-Y-1位置一定为P或G
                    dp[i][1]=sum-dp[i-Y-1][0]-dp[i-Y-1][2];

 
#include <iostream>
#include <algorithm>
#include <string.h>
#include <stdio.h>
using namespace std;
int N, M, K;
const long long mod = 1e9+7;
const int maxn = 1e6+10;
long long dp[maxn][5];
long long ans(int m, int k){
	long long 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])%mod;
		//i处放P士兵;	
		dp[i][2]=sum;
		//i处放G士兵; 
		if(i <= m) dp[i][0]=sum;
		if(i == m+1) dp[i][0]=(sum-1+mod)%mod;
		if(i > m+1) dp[i][0]=(sum-dp[i-m-1][1]-dp[i-m-1][2]+mod)%mod;
		//i处放R士兵; 
		if(i <= k) dp[i][1]=sum;
		if(i == k+1) dp[i][1]=(sum-1+mod)%mod;
		if(i > k+1) dp[i][1]=(sum-dp[i-k-1][0]-dp[i-k-1][2]+mod)%mod;
	}
	return (dp[N][0]+dp[N][1]+dp[N][2])%mod;
}
int main(){
	while(~scanf("%d%d%d", &N, &M, &K)){
		printf("%lld\n", ((ans(N, K)-ans(M-1, K))%mod+mod)%mod);		
	}
	return 0;
} 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值