BZOJ 4934: [Ceoi2016]kangaroo【DP好题】

题目描述:

题目传送门:BZOJ4934
在这里插入图片描述

题目分析:

考虑由小到大填数,显然我们会得到一些M型的链。
想一想如何满足波浪型的限制。
如果我们当前放的数在最终排列中是波峰,那么由于是从小到大填数,它左右两边必然已经填好了,所以它的作用就是合并两条M型的链,得到一条新的M型链。
如果我们当前放的数在最终排列中是波谷,那么它左右两边的数还没有确定,所以我们得到一个长为1的新链。
于是DP状态就比较好想了,用 f [ i ] [ j ] f[i][j] f[i][j]表示填了前 i i i个数,形成了 j j j条M型链的方案数。
转移就如上述所示,但是注意一些特殊情况,譬如i==s合并时链数不会减少,总之就是关于s和t的合并的诸多情况,可以参考代码理解。

Code:
这份代码为了合并时的方便,没有将s链和t链算在j中(也是网上常见的做法):

#include <bits/stdc++.h>
#define maxn 2005
using namespace std;
const int mod = 1e9+7;
int n,s,t,f[2][maxn];
int main(){
	scanf("%d%d%d",&n,&s,&t);
	int now=0; f[now][0]=1;
	for(int i=1;i<n;i++,now=!now){//注意i的循环只到n-1,因为最后s,t已经成型,j=0。
		memset(f[!now],0,sizeof f[!now]);
		for(int j=0;j<i;j++) if(f[now][j]){
			if(i!=s&&i!=t){
				f[!now][j+1]=(f[!now][j+1]+f[now][j])%mod;
				if(j) f[!now][j-1]=(f[!now][j-1]+1ll*f[now][j]*(j*(j-1+(i>s)+(i>t))))%mod;
			}
			else{//s,t aren't inclueded in the 'j'
				f[!now][j]=(f[!now][j]+f[now][j])%mod;
				if(j) f[!now][j-1]=(f[!now][j-1]+1ll*f[now][j]*j)%mod;
			}
		}
	}
	printf("%d\n",f[now][0]);
}

下面这份代码是将s链和t链也算在了j中,比较好理解,但是特判会比较多(我调了40min…)

#include <bits/stdc++.h>
#define maxn 2005
using namespace std;
const int mod = 1e9+7;
int n,s,t,f[2][maxn];
int main(){
	scanf("%d%d%d",&n,&s,&t);
	int now=0,mx=max(s,t); f[now][1]=1;
	for(int i=2;i<=n;i++,now=!now){
		memset(f[!now],0,sizeof f[!now]);
		for(int j=1;j<i;j++) if(f[now][j]){
			f[!now][j+1]=(f[!now][j+1]+f[now][j])%mod;
			if(i!=s&&i!=t) f[!now][j-1]=(f[!now][j-1]+1ll*f[now][j]*((j-(i>s)-(i>t))*(j-1)+(i==n)))%mod;
			else f[!now][j]=(f[!now][j]+1ll*f[now][j]*(j-(i==mx&&i!=n)))%mod;
		}
	}
	printf("%d\n",f[now][1]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值