首页 > 代码库 > NOIp模拟1 Permutation

NOIp模拟1 Permutation

 

试题描述

将 1 到 N 任意排列,然后在排列的每两个数之间根据他们的大小关系插入“>”和“<”。
问在所有排列中,有多少个排列恰好有K个“<”。
例如排列(3, 4, 1, 5, 2)
3 < 4 > 1 < 5 > 2
共有2个“<”

输入格式

N,K

输出格式

答案

输入示例

5 2

输出示例

66

注释说明

20%数据:N <= 10
50%数据:答案在0..2^63-1内
100%数据:K < N <= 100

 

【分析】

dp[i][j]表示在前i个数中插入j个‘<’的方案数。

由于按顺序插入,所以每次插入的数都是当前序列中最大的,首先考虑在被‘<‘连接的两个数之间插入,这时‘<‘的数目不会改变,注意在序列的最左边插入也是一样的效果,所以我们得到了dp方程的第一项dp[i-1][j]*(j+1)。

再考虑在被‘>‘连接的两个数间插入,这时‘<‘数目会增加一,当i个数中有j个‘<‘时,‘>‘的数目为i-1-j,但与上面那种情况同理,在序列的最左端插入也会多一个‘<‘,所以我们得到了dp方程的第二项dp[i-1][j-1]*(i-j)。

最后得到的方程为dp[i][j]=dp[i-1][j]*(j+1)+dp[i-1][j-1]*(i-j)。

初始化条件为dp[i][0]=1(1<=i<=n),即当前序列为严格下降序列。

下面是50分程序,还没写高精度,高精度的实现方法后续会另写一篇文章总结。

 

【代码】

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 long long dp[105][105];
 5 int n, k;
 6 
 7 int main() {
 8     cin >> n >> k;
 9     for (int i=1;i<=n;++i) dp[i][0]=1;
10     for (int i=1;i<=n;++i)
11         for (int j=1;j<=min(k, i-1);++j)
12             dp[i][j]=dp[i-1][j]*(j+1)+dp[i-1][j-1]*(i-j);
13     cout << dp[n][k] << endl;
14 }

 

 

【代码】

NOIp模拟1 Permutation