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

  • 相关阅读:
    asp.net 遍历xml 及 Repeater 绑定xml
    通过NetworkIsolationEnumAppContainers查看安装的UWP应用
    修复 Outlook 数据文件(.pst 和 .ost)
    c# 学习笔记 抓包解析器开发
    c++ 结构体位域操作 进制转换
    《C++ Footprint and Performance Optimization》读书笔记
    SQL SERVER 2000 函数一点小注意
    一个B/S结构MIS的登录日志的问题。
    jmeter连接mysql提示Cannot create PoolableConnectionFactory(查看jmeter日志,提示SSL)如何解决
    List、Map、Set之间的联系与区别:
  • 原文地址:https://www.cnblogs.com/antiquality/p/9373821.html
Copyright © 2011-2022 走看看