DTOJ 4030: 排列计数

Posted ssoiroor

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DTOJ 4030: 排列计数相关的知识,希望对你有一定的参考价值。

【题目描述】

求有多少个$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
",f[n][k]);
    return 0;
}

总结:

计数类问题,一般都是排列组合DP。尤其是在数据范围不太大,DP状态可以表示时,要考虑DP。

其实也不能算是DP,更准确的说应该是递推,考虑从$i$到$i+1$的答案变化。

以上是关于DTOJ 4030: 排列计数的主要内容,如果未能解决你的问题,请参考以下文章

dtoj#4258. 铃铛计数问题

dtoj3433. 优美的字符串(str)

DTOJ #2596. Greenhouse Growth

DTOJ2702:余数

html 将以编程方式附加外部脚本文件的javascript代码片段,并按顺序排列。用于响应式网站,其中ma

[SDOI2016]排列计数