Skip to content

DTOJ 4030 排列计数 - Roor - 博客园

约 526 字大约 2 分钟

DP

2018-02-14

题面

【题目描述】

求有多少个1到 n的排列满足恰有$ k$ 对在排列中相邻的数满足前小于后,答案对2012取模。

【输入】

一行2个正整数$ n , k$ 。

【输出】

输出一个整数表示答案。

【样例输入】

5 2

【样例输出】

66

【数据范围】

k<n<=1000k<n<=1000

题解

计数类问题,应该是个 式子 或者 DP

考虑kknn都不大考虑DP。f[i][j]f[i][j]表示n=in=ik=jk=j时的答案。

那么考虑怎么转移,考虑将ii插入长度为i1i-1的排列中,对答案的影响。有f[i][j]=f[i1][j](j+1)+f[i1][j1](ij)f[i][j]=f[i-1][j]*(j+1)+f[i-1][j-1]*(i-j)

f[i1][j](j+1)f[i-1][j]*(j+1)表示将ii插入后满足要求的数对没有变多,因为ii大于i1i-1排列中的任意一个,所以将ii插入jj个以满足的数对中的任意一个都不会使得满足条件的数对变多,又或者直接将ii放在第一个。

f[i1][j1](ij)f[i-1][j-1]*(i-j)表示将ii插入后数对变多了,也是因为ii大于i1i-1排列中的任意一个,所以只要不插入到j1j-1个已经满足的数对中即可,那么就会有(i2)(j1))(i-2)-(j-1)),加上最后一个位置就是iji-j了。

初值为f[i][0]=1f[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,更准确的说应该是递推,考虑从iii+1i+1的答案变化。