hdu 3664(第二欧拉数,HIT2011春省赛选拔赛通过,hdu G++ 46MS, C++ 31MS通过)

今天很悲剧的省赛选拔赛,早上我们几个都是只做出了海狸啃木头那道博弈,下午倒是还可以,不过相对来讲实验这边基本功还是比不过计院那边扎实啊!跟计院的同学相比实力差了不少。

 

下午的B题对应的是hdu 3664 permutation counting。比赛完后我仔细在网上找了找关于此题的状态转移方程的解题说明,发现基本只有这么一种做法,而且很明显可以看出来都是互相抄的,连题意中的汉语语病都没有改,直接抄了啊!

 

找到这里一个http://hi.baidu.com/ecjtuqx/blog/item/2ec3d9c5d19091a78326aca4.html,对边界解释的还不错,也跟其他人的代码有不同之处,不过对于此题的状态转移方程,我想很多人看了也看不明白,简单的在这里写写。

先贴题目:

 

 

 

Problem Description
Given a permutation a1, a2, … aN of {1, 2, …, N}, we define its E-value as the amount of elements where ai > i. For example, the E-value of permutation {1, 3, 2, 4} is 1, while the E-value of {4, 3, 2, 1} is 2. You are requested to find how many permutations of {1, 2, …, N} whose E-value is exactly k.
 

 

Input
There are several test cases, and one line for each case, which contains two integers, N and k. (1 <= N <= 1000, 0 <= k <= N).
 

 

Output
Output one line for each case. For the answer may be quite huge, you need to output the answer module 1,000,000,007.
 

 

Sample Input
3 0 3 1
 

 

Sample Output
1 4
Hint
There is only one permutation with E-value 0: {1,2,3}, and there are four permutations with E-value 1: {1,3,2}, {2,1,3}, {3,1,2}, {3,2,1}

 

关于边界的理解,参考上面那篇百度空间里的说明,这里讲讲状态转移方程的问题。

网上其他人给的解释是这样的:

 

 

题意:dp[n,k] 表示 n 个数有 k 个 ai>i,考虑第 n 个数,数 n 与前面任何一个 ai<=i 的交换,E-value 增大 1。
数 n 与前面任何一个 ai>i 的交换或者 an = n,E-value不变。
dp[n,k] = (n-1-(k-1))*dp[n-1,k-1] + (k+1)*dp[n-1,k] = (n-k)*dp[n-1,k-1] + (k+1)*dp[n-1,k]

 

 

这里理解一下,对于dp[n,k]来讲,它相对于dp[n-1,?]来讲,新加入的一个数必然是n,即 an = n!这点很容易理解,dp[n-1,?]是针对数组1,2,3,...,n-1,而对于dp[n,k]而言,所针对的数组是1,2,...,n,也就是说,加数后数组的值应当跟n在新数组中的位置有关,这样就能理解上面所讲的“考虑第n个数”了。

 

将an放在第n位时,E-value没有任何改变,此时dp[n,k] = dp[n - 1, k] * 1 + ?

 

将an放在前n-1位又有两种方法,首先是放在所有ai > i 的地方,可以看到,an > i显然成立,而ai < n也显然成立,也就是说,将an与ai > i置换后,E-value仍然没有改变,这种放法可以由dp[n-1,k]的下标看出,前n-1个树中一共有k个数可以被置换,此时dp[n,k] = dp[n - 1, k] * 1 + dp[n - 1, k] * k + ?

 

考虑最后一种放法,将an插入后与前n - 1个数中某一 ai <= i 的数相置换,显然,由 an = n, ai <= i 变换为 an > i, ai < n 就有 E-value + 1,如前法分析之,E-value + 1 后为 k,则此时dp[n,k]与dp[n-1,k-1]相关,显然,对于dp[n-1,k-1]而言,前 n - 1 个数中满足 ai <= i 的共有 n - 1 - (k - 1) = n - k个数,因此

dp[n,k] = dp[n - 1, k] * 1 + dp[n - 1,k] * k + dp[n - 1, k - 1] * (n - k) = (k + 1) * dp[n-1,k] + (n - k) * dp[n-1,k-1]

 

代码如下:

 

 

/* This Code is Submitted by yankaifyyy for Problem 101709 at 2011-05-01 15:46:42 */ #include<iostream> #include<memory> using namespace std; int n, m; long long dp[1005][1005]; int main() { memset(dp, 0, sizeof (dp)); for (int i = 1; i <= 1000; i++) dp[i][0] = 1; for (int i = 2; i <= 1000; i++) for (int j = 1; j < i; j++) dp[i][j] = (dp[i - 1][j]*(j + 1) % 1000000007 + dp[i - 1][j - 1]*(i - j) % 1000000007) % 1000000007; while (cin >> n >> m) { cout << dp[n][m] << endl; } return 0; }

 

这道题分析的重点在于方程的建立,而对于具体的DP过程并没有什么值得优化的,虽然最终数值会很大,但1~1000的数据域并不算太大。写这篇解题报告,一是为部分仍然无法理解网上给的简练的方程的同学帮助消化;二来,也算是为自己应该能参加省赛(毕竟才大一么,省赛已经不错了哈)写个开头吧。

你可能感兴趣的:(C++,Module,input,each,output,permutation)