bzoj 4321 queue2 - 动态规划

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 4321 queue2 - 动态规划相关的知识,希望对你有一定的参考价值。

n 个沙茶,被编号 1~n。排完队之后,每个沙茶希望,自己的相邻的两
人只要无一个人的编号和自己的编号相差为 1(+1 或-1)就行; 
现在想知道,存在多少方案满足沙茶们如此不苛刻的条件。 

Input

只有一行且为用空格隔开的一个正整数 N,其中 100%的数据满足 1≤N ≤ 1000; 

Output

一个非负整数,表示方案数对 7777777 取模。   

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 - 动态规划的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 4321: queue2

BZOJ4321queue2 DP

BZOJ4321: queue2

bzoj4321: queue2(DP)

B4321 queue2 dp

题解 bzoj1207: [HNOI2004]打鼹鼠 (动态规划)