传送门

参考博文:

  [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 的最长上升子序列的长度

 for(int i=;i < <<;++i)
{
lis[i]=;
for(int j=;j < ;++j)
lis[i] += (i&(<<j)) ? :;
}

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

 int LIS(int state,int num)
{
for(int i=num;i < ;++i)
if(state&(<<i))
return (state^(<<i))|(<<num);
return state|(<<num);
}

  笼统的解释一下就是:插入 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 的总个数;

  核心代码:

 ll DFS(int curPos,int state,bool lead,bool limit)
{
if(curPos == -)
return lis[state] == K ? :;
if(!limit&&dp[curPos][state][K] != -)
return dp[curPos][state][K]; int up=limit ? digit[curPos]:;
ll ans=;
for(int i=;i <= up;++i)
ans += DFS(curPos-,lead&&i== ? :LIS(state,i),lead&&i==,limit&&i==digit[curPos]); if(!limit)
dp[curPos][state][K]=ans; return ans;
}

  1~9行不再赘述;

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

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

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

AC代码:

 #include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a)) ll N,M;
int K;
int digit[];
ll dp[][<<][];
int lis[<<]; int LIS(int state,int num)
{
for(int i=num;i < ;++i)
if(state&(<<i))
return (state^(<<i))|(<<num);
return state|(<<num);
}
ll DFS(int curPos,int state,bool lead,bool limit)
{
if(curPos == -)
return lis[state] == K ? :;
if(!limit&&dp[curPos][state][K] != -)
return dp[curPos][state][K]; int up=limit ? digit[curPos]:;
ll ans=;
for(int i=;i <= up;++i)
ans += DFS(curPos-,lead&&i== ? :LIS(state,i),lead&&i==,limit&&i==digit[curPos]); if(!limit)
dp[curPos][state][K]=ans; return ans;
}
ll Solve(ll x)
{
int k=;
while(x)
{
digit[k++]=x%;
x /= ;
}
return DFS(k-,,true,true);
}
int main()
{
int test;
scanf("%d",&test);
mem(dp,-); for(int i=;i < <<;++i)
{
lis[i]=;
for(int j=;j < ;++j)
lis[i] += (i&(<<j)) ? :;
}
for(int kase=;kase <= test;++kase)
{
scanf("%lld%lld%d",&N,&M,&K);
printf("Case #%d: %lld\n",kase,Solve(M)-Solve(N-));
}
return ;
}

  

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

hdu 4352 "XHXJ's LIS"(数位DP+状压DP+LIS)的更多相关文章

  1. 【BZOJ】1076 [SCOI2008]奖励关 期望DP+状压DP

    [题意]n种宝物,k关游戏,每关游戏给出一种宝物,可捡可不捡.每种宝物有一个价值(有负数).每个宝物有前提宝物列表,必须在前面的关卡取得列表宝物才能捡起这个宝物,求期望收益.k<=100,n&l ...

  2. HDU 3920Clear All of Them I(状压DP)

    HDU 3920   Clear All of Them I 题目是说有2n个敌人,现在可以发n枚炮弹,每枚炮弹可以(可以且仅可以)打两个敌人,每一枚炮弹的花费等于它所行进的距离,现在要消灭所有的敌人 ...

  3. CCF 201312-4 有趣的数 (数位DP, 状压DP, 组合数学+暴力枚举, 推公式, 矩阵快速幂)

    问题描述 我们把一个数称为有趣的,当且仅当: 1. 它的数字只包含0, 1, 2, 3,且这四个数字都出现过至少一次. 2. 所有的0都出现在所有的1之前,而所有的2都出现在所有的3之前. 3. 最高 ...

  4. HDU 5067 Harry And Dig Machine(状压dp)

    HDU 5067 Harry And Dig Machine 思路:因为点才10个,在加上一个起点,处理出每一个点之间的曼哈顿距离,然后用状压dp搞,状态表示为: dp[i][s],表示在i位置.走过 ...

  5. HDU - 6125: Free from square (状压DP+分组背包)

    problem:给定N,K.表示你有数1到N,让你最多选择K个数,问有多少种方案,使得选择的数的乘积无平方因子数.N,K<500: solution:显然可以状压DP做,但是500以内的素数还是 ...

  6. HDU 3247 Resource Archiver(AC自动机 + 状压DP + bfs预处理)题解

    题意:目标串n( <= 10)个,病毒串m( < 1000)个,问包含所有目标串无病毒串的最小长度 思路:貌似是个简单的状压DP + AC自动机,但是发现dp[1 << n][ ...

  7. HDU 6984 - Tree Planting(数据分治+状压 dp)

    题面传送门 傻逼卡常屑题/bs/bs,大概现场过得人比较少的原因就是它比较卡常罢(Fog 首先对于这样的题我们很难直接维护,不过注意到这个 \(n=300\) 给得很灵性,\(k\) 比较小和 \(k ...

  8. [转]状态压缩dp(状压dp)

    状态压缩动态规划(简称状压dp)是另一类非常典型的动态规划,通常使用在NP问题的小规模求解中,虽然是指数级别的复杂度,但速度比搜索快,其思想非常值得借鉴. 为了更好的理解状压dp,首先介绍位运算相关的 ...

  9. 状态压缩dp 状压dp 详解

    说到状压dp,一般和二进制少不了关系(还常和博弈论结合起来考,这个坑我挖了还没填qwq),二进制是个好东西啊,所以二进制的各种运算是前置知识,不了解的话走下面链接进百度百科 https://baike ...

随机推荐

  1. Yii2的客户端验证

    如何配置Yii的客户端验证呢? 首先,应该配置验证规则的场景,即scenario 其次,应该配置验证规则,在验证规则中配置客户端验证 例如:

  2. 1064 - You have an error in your SQL syntax;

    mysql 1064 错误: SQL语法错误,check the manual that corresponds to your MySQL server version for the right ...

  3. Scss - 简单笔记

    原文链接:scss 教程 手头上疯狂在用 scss,虽然可以在里面写原生的 css, 但是为了保持风格的一致性,还是滚去看了看 scss 文档. 一.变量 变量的引入是 scss 的一个核心特性,变量 ...

  4. Windows & RabbitMQ:集群(clustering) & 高可用(HA)

    描述:我们需要配置三台服务器:ServerA, ServerB, ServerC 注意事项: 所有的服务器的Erlang版本,RabbitMQ版本必须一样 服务器名大小写敏感 Step 1:安装Rab ...

  5. Git秘钥生成以及Gitlab配置

    安装Git:详见http://www.cnblogs.com/xiuxingzhe/p/9300905.html 开通gitlab(开通需要咨询所在公司的gitlab管理员)账号后,本地Git仓库和g ...

  6. BZOJ3502PA2012Tanie linie&BZOJ2288[POJ Challenge]生日礼物——模拟费用流+链表+堆

    题目描述 n个数字,求不相交的总和最大的最多k个连续子序列. 1<= k<= N<= 1000000. 输入 输出 样例输入 5 2 7 -3 4 -9 5 样例输出 13   根据 ...

  7. Codeforces Round #433 Div. 1

    A:显然从大到小排序后贪心放在第一个能放的位置即可.并查集维护. #include<iostream> #include<cstdio> #include<cmath&g ...

  8. 反射 与 preprrty装饰器

    反射: 指的是通过字符串来操作对象属性的增删改除 主要分为四种方法: hasattr :    查看字符串 是否 存在 返回---> Ture / False getattr:    获取对象属 ...

  9. Matplotlib学习---matplotlib的一些基本用法

    Matplotlib有两种接口,一种是matlab风格接口,一种是面向对象接口.在这里,统一使用面向对象接口.因为面向对象接口可以适应更复杂的场景,在多图之间进行切换将变得非常容易. 首先导入matp ...

  10. 不可解问题之停机问题(Undecidable Problem Halting Problem)

    计算机技术已运用到人类生活的方方面面,帮助人类解决各种问题.可你是否有想过,计算机是否能为人类解决所有问题呢? 假如你是一个程序猿,你已编写过很多程序.有些程序一下子就能出结果,有些程序则好久都没有显 ...