zoukankan      html  css  js  c++  java
  • hdu 4352 "XHXJ's LIS"(数位DP+状压DP+LIS)

    传送门

    参考博文:

      [1]:http://www.voidcn.com/article/p-ehojgauy-ot.html

    题解:

      将数字num字符串化;

      求[L,R]区间最长上升子序列长度为 K 的总个数;

    题解:

      也不算是题解,只是谈谈我对此题解法的理解;

      学到数位DP的话,应该已经学过状压DP 和 LIS O( nlog(n) )解法吧(默认学过了);

      对于此题,一共就10个不同的数字 "0~9",对于长度为 K 的最长上升子序列,如果不适用记忆化搜索的话,一定会重复计算好多好多次;

      例如,如果 K = 10,那么,最长上升子序列一定顺序包含 0 1 2 3 4 5 6 7 8 9 这十个数字,而最多可以达到19位;

      那么,除这十位外,还有 9 位可以由 0~9 的任意数字随机排列组成,那么重复计算的次数是非常多的。

      关键就是如何记录已经求过的状态?

      首先介绍一下此题如何使用状压;

      正如上例,我们只需关注 0~9 这十个数字是否出现过,以及是否按顺序出现,而不必关心其他位数是由什么组成的,

      那么,这就要用到状压DP的思想了;

      对于 0~9 这十位数:

      9  8  7  6  5  4  3  2  1  0

      _  _  _  _  _  _  _  _  _  _

      每个数字下的横杠可填 0 或 1 这两个数;

      0:不含当前数字;

      1:含有当前数字;

      那么,对于状态 state ,判断其是否含有数字 i ,只需判断 state&(1<<i) 是否为 1 即可;

      首先是预处理:

      定义 lis[ i ] : 状态 i 的最长上升子序列的长度

    1 for(int i=0;i < 1<<10;++i)
    2 {
    3     lis[i]=0;
    4     for(int j=0;j < 10;++j)
    5         lis[i] += (i&(1<<j)) ? 1:0;
    6 }

      接下来,看看这个代码的作用:

    1 int LIS(int state,int num)
    2 {
    3     for(int i=num;i < 10;++i)
    4         if(state&(1<<i))
    5             return (state^(1<<i))|(1<<num);
    6     return state|(1<<num);
    7 }

      笼统的解释一下就是:插入 num 后,将 state 中的第一个大于等于 num 位的 1 变为 0,并将 num 位 置为 1;

      例如,假设 state = (1,000,110,001)(2);

      可得,当前 state 已经形成了由 0,4,5,9 构成的长度为 4 的最长上升子序列;

      如果当前加入的数字为 3 ,根据 LIS 的思想,在之前形成的最长上升子序列中找到第一个大于等于 3 的数,

      并将3和其互换,那么返回的值就是 (1,000,101,001)(2);

      那么,这段代码到底是干啥用的呢?

      定义dp[ curPos ][ state ][ K ] : 来到curPos 位置时,状态为 state 所形成的最长上升子序列长度为 K 的总个数;

      核心代码:

     1 ll DFS(int curPos,int state,bool lead,bool limit)
     2 {
     3     if(curPos == -1)
     4         return lis[state] == K ? 1:0;
     5     if(!limit&&dp[curPos][state][K] != -1)
     6         return dp[curPos][state][K];
     7 
     8     int up=limit ? digit[curPos]:9;
     9     ll ans=0;
    10     for(int i=0;i <= up;++i)
    11         ans += DFS(curPos-1,lead&&i==0 ? 0:LIS(state,i),lead&&i==0,limit&&i==digit[curPos]);
    12 
    13     if(!limit)
    14         dp[curPos][state][K]=ans;
    15 
    16     return ans;
    17 }

      1~9行不再赘述;

      主要看一下第11行的作用;

      第二个参数赋的值为 lead&&i==0 ? 0:LIS(state,i) ,给下一个状态赋 0 很好理解,关键是 LIS(state,i);

      这就对应着上述对 LIS() 的解释;

    AC代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 #define ll long long
     6 #define mem(a,b) memset(a,b,sizeof(a))
     7 
     8 ll N,M;
     9 int K;
    10 int digit[20];
    11 ll dp[20][1<<10][20];
    12 int lis[1<<10];
    13 
    14 int LIS(int state,int num)
    15 {
    16     for(int i=num;i < 10;++i)
    17         if(state&(1<<i))
    18             return (state^(1<<i))|(1<<num);
    19     return state|(1<<num);
    20 }
    21 ll DFS(int curPos,int state,bool lead,bool limit)
    22 {
    23     if(curPos == -1)
    24         return lis[state] == K ? 1:0;
    25     if(!limit&&dp[curPos][state][K] != -1)
    26         return dp[curPos][state][K];
    27 
    28     int up=limit ? digit[curPos]:9;
    29     ll ans=0;
    30     for(int i=0;i <= up;++i)
    31         ans += DFS(curPos-1,lead&&i==0 ? 0:LIS(state,i),lead&&i==0,limit&&i==digit[curPos]);
    32 
    33     if(!limit)
    34         dp[curPos][state][K]=ans;
    35 
    36     return ans;
    37 }
    38 ll Solve(ll x)
    39 {
    40     int k=0;
    41     while(x)
    42     {
    43         digit[k++]=x%10;
    44         x /= 10;
    45     }
    46     return DFS(k-1,0,true,true);
    47 }
    48 int main()
    49 {
    50     int test;
    51     scanf("%d",&test);
    52     mem(dp,-1);
    53 
    54 for(int i=0;i < 1<<10;++i)
    55 {
    56     lis[i]=0;
    57     for(int j=0;j < 10;++j)
    58         lis[i] += (i&(1<<j)) ? 1:0;
    59 }
    60     for(int kase=1;kase <= test;++kase)
    61     {
    62         scanf("%lld%lld%d",&N,&M,&K);
    63         printf("Case #%d: %lld
    ",kase,Solve(M)-Solve(N-1));
    64     }
    65     return 0;
    66 }
    View Code

      

      当然,也可提前预处理出状态 i 插入数字 j 后形成的状态,这就对应着参考博文中的 nex[][] 数组;

  • 相关阅读:
    Codeforces 1105D Kilani and the Game【BFS】
    Codeforces 1096D Easy Problem 【DP】
    Codeforces 920F
    Codeforces 1076D Edge Deletion 【最短路+贪心】
    POJ 3090 Visible Lattice Points 【欧拉函数】
    POJ 1284 Primitive Roots (欧拉函数+原根)
    HDU 2841-Visible Trees 【容斥】
    HDU 1796 How many integers can you find 【容斥】
    HDU 4135 Co-prime (容斥+分解质因子)
    CodeForces 161D Distance in Tree【树形DP】
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/10609649.html
Copyright © 2011-2022 走看看