DTOJ 4030 排列计数 - Roor - 博客园
题面
【题目描述】
求有多少个1到 n的排列满足恰有$ k$ 对在排列中相邻的数满足前小于后,答案对2012取模。
【输入】
一行2个正整数$ n , k$ 。
【输出】
输出一个整数表示答案。
【样例输入】
5 2
【样例输出】
66
【数据范围】
k<n<=1000
题解
计数类问题,应该是个 式子 或者 DP 。
考虑k和n都不大考虑DP。f[i][j]表示n=i,k=j时的答案。
那么考虑怎么转移,考虑将i插入长度为i−1的排列中,对答案的影响。有f[i][j]=f[i−1][j]∗(j+1)+f[i−1][j−1]∗(i−j)
f[i−1][j]∗(j+1)表示将i插入后满足要求的数对没有变多,因为i大于i−1排列中的任意一个,所以将i插入j个以满足的数对中的任意一个都不会使得满足条件的数对变多,又或者直接将i放在第一个。
f[i−1][j−1]∗(i−j)表示将i插入后数对变多了,也是因为i大于i−1排列中的任意一个,所以只要不插入到j−1个已经满足的数对中即可,那么就会有(i−2)−(j−1)),加上最后一个位置就是i−j了。
初值为f[i][0]=1,可以用打表和DP式子来判断。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 1005
#define p 2012
using namespace std;
int n,k,f[N][N];
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) f[i][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++)
f[i][j]=(f[i-1][j]*(j+1)%p+(f[i-1][j-1]*(i-j))%p)%p;
printf("%d\n",f[n][k]);
return 0;
}
总结
计数类问题,一般都是 排列组合 和 DP 。尤其是在数据范围不太大,DP状态可以表示时,要考虑DP。
其实也不能算是DP,更准确的说应该是递推,考虑从i到i+1的答案变化。