zoukankan      html  css  js  c++  java
  • 【dp 状态压缩 单调栈】bzoj3591: 最长上升子序列

    奇妙的单调栈状压dp

    Description

    给出1~n的一个排列的一个最长上升子序列,求原排列可能的种类数。

    Input

    第一行一个整数n。
    第二行一个整数k,表示最长上升子序列的长度。
    第三行k个整数,表示这个最长上升子序列。

    Output

    第一行一个整数,表示原排列可能的种类数。

    Sample Input

    5
    3
    1 3 4

    Sample Output

    11

    HINT

    【样例说明】

    11种排列分别为(1, 3, 2, 5, 4), (1, 3, 5, 2, 4), (1, 3, 5, 4, 2), (1, 5, 3, 2, 4), (1, 5, 3, 4, 2), (2, 1, 3, 5, 4), (2, 1, 5, 3, 4), (2, 5, 1, 3, 4), (5, 1, 3, 2, 4), (5, 1, 3, 4, 2), (5, 2, 1, 3, 4)。

    【数据规模和约定】

    对于30%的数据,1 <= n <= 11。

    对于70%的数据,1 <= n <= 14。

    对于100%的数据,1 <= n <= 15,答案小于2^31。


    题目分析

    这种属于奇奇怪怪的状压dp:把单调栈拿来状压。

    考虑求解LIS,是一个使用单调栈的过程。因此按序列位置依次考虑,每一个数有三种状态:0:没有被考虑;1:考虑了,在栈里;2:考虑了,已出栈————自然可以三进制状压表示。

    再考虑dp的过程:用$f[i]$表示$i$这个三进制状态下的合法方案数。转移时则从小到大枚举每一个数,看其是否能够通过当前状态合法转移至下一个LIS(注意判断转移前的合法性)。

    转移不难,重点在于想到状压每个数对于单调栈的状态。

     1 #include<bits/stdc++.h>
     2 const int maxn = 23;
     3 
     4 int val[maxn],base[maxn],n,m;
     5 int num[maxn],pre[maxn];
     6 int LIS[maxn],cnt;
     7 int f[14349003],ans;      //f[]不要忘记开大,表示3^n种状态
     8 
     9 int read()
    10 {
    11     char ch = getchar();
    12     int num = 0;
    13     bool fl = 0;
    14     for (; !isdigit(ch); ch = getchar())
    15         if (ch=='-') fl = 1;
    16     for (; isdigit(ch); ch = getchar())
    17         num = (num<<1)+(num<<3)+ch-48;
    18     if (fl) num = -num;
    19     return num;
    20 }
    21 int main()
    22 {
    23     n = read(), m = read(), num[0] = 0;
    24     for (int i=1; i<=m; i++)
    25     {
    26         num[i] = read()-1, pre[num[i]] = i-1;
    27         if (num[i] < num[i-1]){
    28             puts("0");
    29             return 0;
    30         }
    31     }
    32     base[0] = 1;
    33     for (int i=1; i<=n; i++) base[i] = base[i-1]*3;
    34     f[0] = 1;
    35     for (int i=0; i<base[n]; i++)
    36         if (f[i]){
    37             int st = i,done,cnt,ins;
    38             done = cnt = ins = 0;
    39             for (int j=0; j<n; j++)
    40             {
    41                 val[j] = st%3, st /= 3;
    42                 if (val[j]) done++;
    43                 if (val[j]==1) LIS[cnt++] = j;
    44             }
    45             if (done==n){
    46                 ans += f[i];
    47                 continue;
    48             }
    49             for (int j=0; j<n; j++)
    50             {
    51                 if (val[j]) continue;
    52                 if (pre[j]&&!val[num[pre[j]]]) continue;
    53                 while (LIS[ins] < j&&ins < cnt) ins++;
    54                 if (ins==m) continue;
    55                 int nxt = i+base[j];
    56                 if (ins < cnt) nxt += base[LIS[ins]];
    57                 f[nxt] += f[i];
    58             }
    59         }
    60     printf("%d
    ",ans);
    61     return 0;
    62 }

    END

  • 相关阅读:
    Java中的事务
    ABCDE
    Android 防内存泄露handler
    自建应用新花样,菜鸟也会做应用
    软件測试之独步武林系列(一)
    刚在在win8.1下装了ubuntu12.04
    SVN 的一些操作
    [华为机试练习题]42.求二叉树的深度和宽度
    iOS_正則表達式
    在应用中更新App版本号
  • 原文地址:https://www.cnblogs.com/antiquality/p/9373821.html
Copyright © 2011-2022 走看看