bzoj 4321 queue2 - 动态规划
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 4321 queue2 - 动态规划相关的知识,希望对你有一定的参考价值。
Input
Output
Sample Input
4
Sample Output
2 样例解释:有两种方案 2 4 1 3 和 3 1 4 2
题目大意 问n个数的排列且满足任意两个相邻的数的差都大于1的个数。
显然dp。(难道还能爆搜?)
现在来设计状态和转移。因为状态和转移都不好想,所以根据常用套路猜测一下。
1)f[i][j]表示已经确定了前i个数,最后一个数是j的合法方案数。看似很棒,然而你知道可行字符集吗?当然不知道。那它能存下来吗?当然不能(要是能存下来,和爆搜有区别吗?)因此,只能另外思考。
2)f[i]表示n个位置中已经填完1 - i这几个数。然而发现。。转移的话同样需要很多东西。。果断放弃
3)考虑将排列当成一个动态数组,f[i]表示当n = i时的答案。
考虑转移。然而总感觉少了些什么。。肿么办?加状态!
于是便有了f[i][j]表示已经填了1 ~ i,其中包含j个不满足条件的对子。
似乎转移轻松了些。然而很快又发现了不对劲。考虑将(i + 1)插入排列,例如当i = 3时,排列 1 3 2变成排列 1 4 3 2 和排列 1 3 4 2 的时候增加的不满足条件的对子有点不一样,情况也不同于排列 3 1 2 将4加在3两端。
所以根据观察可以得出应该增加的状态应该是i是否和(i - 1)相邻。
现在开始讨论几种情况。(下面讨论的转移请自行取模)
//假设读者的小学数学没有问题
i.对于f[i][j][0]的转移
a.首先是将(i + 1)放在i的两端,这样会导致增加1个不合法的对子。故有这么一个转移: f[i + 1][j + 1][1] += f[i][j][0] * 2
b.然后是将(i + 1)放在其他地方(这样的一个地方有(i + 1 - j - 2)个,即(i - j - 1)个),使得这样什么都不会发生。所以就有这么一个转移: f[i + 1][j][0] += f[i][j][0] * 1LL * (i - j - 1)
c.最后是将(i + 1)拿去破坏已经有的对子的相对稳定结构(我应该是化学学多了),比如在 2 3 中强行插入一个4,这样就有个转移: f[i + 1][j - 1][0] += f[i][j][0] * 1LL * j
ii.对于f[i][j][1]的转移
a.首先是将(i + 1)放在i和(i - 1)的两端,这样会增加对子i和(i + 1),并且会减少1个对子,故有转移 f[i + 1][j][1] += f[i][j][1]
b.考虑将(i + 1)扔在上一种情况提到的一侧的另一侧,这样只会增加一个对子,故有转移 f[i + 1][j + 1][1] += f[i][j][1]
c.然后考虑将(i + 1)放在不会有影响的地方,这样的地方会有(i + 1 - j - 1)个,即(i - j)个,相信有人一定很好奇为什么情况i.b是(i - j - 1),因为这个地方的j中包含了i的一端,而上面并没有包含(如果一脸雾水请画图吧,个人认为比较显然)。所以有转移 f[i + 1][j][0] += f[i][j][1] * 1LL * (i - j) 。
d.最后仍然是把(i + 1)拿去搞事情。。由于只能拆散对子,所以不能与i相邻,所以只能有(j - 1)个位置可选择。因此有转移 f[i + 1][j - 1][0] += f[i][j][1] * 1ll * (j - 1)
然后整理成程序就好了。
//Q:题目中的"沙茶"是什么鬼 //A:当你对你的父母师长大声说出这个词之后你就会有深刻影响了!
Code
1 /** 2 * bzoj 3 * Problem#4321 4 * Accepted 5 * Time: 272ms 6 * Memory: 9132k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 #ifdef WIN32 11 #define Auto "%I64d" 12 #else 13 #define Auto "%lld" 14 #endif 15 #define ll long long 16 17 const int N = 1002, M = 7777777; 18 19 int n; 20 int f[N][N][2]; 21 22 template<typename T> 23 inline void mod_plus(int& a, T b) { 24 a = (a + b) % M; 25 } 26 27 inline void init() { 28 scanf("%d", &n); 29 } 30 31 inline void solve() { 32 f[1][0][0] = 1; 33 for(int i = 1; i < n; i++) { 34 for(int j = 0; j < i; j++) { 35 mod_plus(f[i + 1][j + 1][1], f[i][j][0] * 2 + f[i][j][1]); 36 mod_plus(f[i + 1][j][1], f[i][j][1]); 37 mod_plus(f[i + 1][j][0], f[i][j][0] * 1LL * (i - j - 1) + f[i][j][1] * 1LL * (i - j)); 38 if(j) mod_plus(f[i + 1][j - 1][0], f[i][j][1] * 1ll * (j - 1) + f[i][j][0] * 1LL * j); 39 } 40 } 41 printf(Auto, f[n][0][0]); 42 } 43 44 int main() { 45 init(); 46 solve(); 47 return 0; 48 }
以上是关于bzoj 4321 queue2 - 动态规划的主要内容,如果未能解决你的问题,请参考以下文章