试题描述 |
将 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 }
【代码】
时间: 2024-11-08 07:17:55