今天很悲剧的省赛选拔赛,早上我们几个都是只做出了海狸啃木头那道博弈,下午倒是还可以,不过相对来讲实验这边基本功还是比不过计院那边扎实啊!跟计院的同学相比实力差了不少。
下午的B题对应的是hdu 3664 permutation counting。比赛完后我仔细在网上找了找关于此题的状态转移方程的解题说明,发现基本只有这么一种做法,而且很明显可以看出来都是互相抄的,连题意中的汉语语病都没有改,直接抄了啊!
找到这里一个http://hi.baidu.com/ecjtuqx/blog/item/2ec3d9c5d19091a78326aca4.html,对边界解释的还不错,也跟其他人的代码有不同之处,不过对于此题的状态转移方程,我想很多人看了也看不明白,简单的在这里写写。
先贴题目:
关于边界的理解,参考上面那篇百度空间里的说明,这里讲讲状态转移方程的问题。
网上其他人给的解释是这样的:
题意: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的数据域并不算太大。写这篇解题报告,一是为部分仍然无法理解网上给的简练的方程的同学帮助消化;二来,也算是为自己应该能参加省赛(毕竟才大一么,省赛已经不错了哈)写个开头吧。