题目描述
将1到n任意排列,然后在排列的每两个数之间根据他们的大小关系插入“>”和“<”。问在所有排列中,有多少个排列恰好有k个“<”。答案对2015取模。
注:1~n的排列指的是1~n这n个数各出现且仅出现一次的数列。
输入输出格式
输入格式:
第一行2个整数n,k。
输出格式:
一个整数表示答案。
输入输出样例
输入样例#1:
5 2
输出样例#1:
66
-
先设计状态,令dp[i][j]表示前i个数有j个小于号。
-
在已有的i个数中,我们可以选择三个位置插入当前数——队首、队尾和中间的i-1个位置共i+1个位置。
-
若插入在队首,因为从小到大插入,所以当前数一定大于队首,会增加一个大于号。
-
若插入在队尾,同理得当前数大于队尾,会增加一个小于号。
-
若插入在中间,不妨设前一个数为x,后一个数为y。
-
当x<y时,x<当前数>y,会增加一个大于号;
-
当x>y时,x<当前数>y,会增加一个小于号;
-
因为共有j个小于号,所以增加一个小于号的方案数为总数-小于号个数-队首的情况,即(i+1)-j-1=i-j种;
-
增加一个大于号的方案数为小于号总数+队尾的情况,即j+1种。
-
状态转移方程就显而易见了——dp[i][j]=dp[i-1][j-1]*(i-j)+dp[i-1][j]*(j+1)。
-
最后就是取模了。
CODING
#include<bits/stdc++.h>
#define mod 2015
using namespace std;
int n,k,dp[1005][1005];
inline int read(void){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
int main(){
n=read();k=read();
for(register int i=1;i<=n;i++){
dp[i][0]=1;
for(register int j=1;j<=k;j++)
dp[i][j]=(((dp[i-1][j-1]%mod)*(i-j)%mod)%mod+((dp[i-1][j]%mod)*(j+1)%mod)%mod)%mod;
}
printf("%d\n",dp[n][k]%mod);
return 0;
}
然后就水过了~