题目描述:
题目分析:
考虑由小到大填数,显然我们会得到一些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]);
}