HDU 4352 XHXJ's LIS(数位DP+状压)

2024-04-16 11:08
文章标签 dp 状压 hdu lis 数位 4352 xhxj

本文主要是介绍HDU 4352 XHXJ's LIS(数位DP+状压),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Problem Description
#define xhxj (Xin Hang senior sister(学姐)) 
If you do not know xhxj, then carefully reading the entire description is very important.
As the strongest fighting force in UESTC, xhxj grew up in Jintang, a border town of Chengdu.
Like many god cattles, xhxj has a legendary life: 
2010.04, had not yet begun to learn the algorithm, xhxj won the second prize in the university contest. And in this fall, xhxj got one gold medal and one silver medal of regional contest. In the next year's summer, xhxj was invited to Beijing to attend the astar onsite. A few months later, xhxj got two gold medals and was also qualified for world's final. However, xhxj was defeated by zhymaoiing in the competition that determined who would go to the world's final(there is only one team for every university to send to the world's final) .Now, xhxj is much more stronger than ever,and she will go to the dreaming country to compete in TCO final.
As you see, xhxj always keeps a short hair(reasons unknown), so she looks like a boy( I will not tell you she is actually a lovely girl), wearing yellow T-shirt. When she is not talking, her round face feels very lovely, attracting others to touch her face gently。Unlike God Luo's, another UESTC god cattle who has cool and noble charm, xhxj is quite approachable, lively, clever. On the other hand,xhxj is very sensitive to the beautiful properties, "this problem has a very good properties",she always said that after ACing a very hard problem. She often helps in finding solutions, even though she is not good at the problems of that type.
Xhxj loves many games such as,Dota, ocg, mahjong, Starcraft 2, Diablo 3.etc,if you can beat her in any game above, you will get her admire and become a god cattle. She is very concerned with her younger schoolfellows, if she saw someone on a DOTA platform, she would say: "Why do not you go to improve your programming skill". When she receives sincere compliments from others, she would say modestly: "Please don’t flatter at me.(Please don't black)."As she will graduate after no more than one year, xhxj also wants to fall in love. However, the man in her dreams has not yet appeared, so she now prefers girls.
Another hobby of xhxj is yy(speculation) some magical problems to discover the special properties. For example, when she see a number, she would think whether the digits of a number are strictly increasing. If you consider the number as a string and can get a longest strictly increasing subsequence the length of which is equal to k, the power of this number is k.. It is very simple to determine a single number’s power, but is it also easy to solve this problem with the numbers within an interval? xhxj has a little tired,she want a god cattle to help her solve this problem,the problem is: Determine how many numbers have the power value k in [L,R] in O(1)time.
For the first one to solve this problem,xhxj will upgrade 20 favorability rate。

Input
First a integer T(T<=10000),then T lines follow, every line has three positive integer L,R,K.(
0<L<=R<2 63-1 and 1<=K<=10).

Output
For each query, print "Case #t: ans" in a line, in which t is the number of the test case starting from 1 and ans is the answer.

Sample Input
  
1 123 321 2

Sample Output
  
Case #1: 139

Author
peterae86@UESTC03

Source
2012 Multi-University Training Contest 6
非常好的一道题,将数位DP 和状压DP以及最长上升子序列结合起来了
题意:
求区间[l,r]内满足将数的每一位取出来当数列看时最长上升子序列为K的数的个数
分析:
很显然的数位DP的调调,但是怎么保存状态呢?
定义dp[i][j][k]:
i 表示当前进行到的数位 (好像基本上数位DP的第一维都是保存的这个)
j 是一个压缩的状态,该状态记录的是一个LIS序列,该状态中的 1 的个数即为最长上升子序列的长度
k 最长上升子序列的长度为 k
dp[i][j][k] 表示满足 i j k的性质的数的个数
特别解释一下状态压缩的 j :
二进制的每一位1表示该位对应的数字是最长上升子序列的一部分,0则不是
比如  【0101010100】这个状态表示的是最长上升子序列是1,3,5,7的数
这算是一个性质,凡是具有这样性质的数都被这一维记录下来了
比如10357和13057都满足这个状态,都属于这样的数
这样将整个最长上升子序列都被记录了下来,其长度就是这个状态里面1的个数
其他细节代码里都有详细注释,这里特别解释一下状态转移的代码
int turn(int s,int x)//状态转移 对状态s加入数字 x
{for (int i = x;i < 10;++i)//类似LIS中的更新末位元素使其变小{if (s&(1<<i)) //将这个比 x 大的数字 i 用 x这个更小的数去更新return (s^(1<<i))|(1<<x);//s^(1<<i)是把 i 这一位变成0, |(1<<x)则将 x 这一位变成 1}//如果 x 比该 LIS序列的最后一个元素都大则可以加入到后面去return s|(1<<x);//直接将这一位变成 1
}

看这部分代码的时候仔细回忆一下最长上升子序列的算法(nlongn版)是怎么实现的
/* Description: 最长上升子序列  定义d[k]:长度为k的上升序列最末元素(终点元素) //注意d中元素是单调递增的 初始化:len = 1,d[1]=a[1], 然后对于a[i]: if a[i] > d[len]   len++,d[len] = a[i] else  from d[1] to d[len]找到一个j 满足 d[j-1]<a[i]<d[j] 更新d[j] = a[i]  
*/ 

这里的状态转移其实是一样的,
比如数列1 3 5 7 :当扫到8的时候,它就变成了1 3 5 7 8,这就相当于if a[i] > d[len]的情况
当扫到4的时候,它就变成了1 3 4 7 ,这就相当于 else 的情况
就是将最长上升子序列的算法放到了压缩的状态上处理,所以说这题经典啊!
其他细节代码中见:
#define mem(a,x) memset(a,x,sizeof(a))
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<stdlib.h>
#include<cctype>
#include<string>
using namespace std;
typedef long long ll;
ll dp[22][1<<10][11];
/*dp[i][j][k]的解释:i 表示当前进行到的数位 (好像基本上数位DP的第一维都是保存的这个)j 是一个压缩的状态,该状态记录的是一个LIS序列,该状态中的 1 的个数即为最长上升子序列的长度k 最长上升子序列的长度为 kdp[i][j][k]满足 i,j,k 的数的个数
*/
int K;
int turn(int s,int x)//状态转移 对状态s加入数字 x
{for (int i = x;i < 10;++i)//类似LIS中的更新末位元素使其变小{if (s&(1<<i)) //将这个比 x 大的数字 i 用 x这个更小的数去更新return (s^(1<<i))|(1<<x);//s^(1<<i)是把 i 这一位变成0, |(1<<x)则将 x 这一位变成 1}//如果 x 比该 LIS序列的最后一个元素都大则可以加入到后面去return s|(1<<x);//直接将这一位变成 1
}
int getlen(int s)//返回该状态表示的最长上升子序列的长度
{int t = 0;while (s){if (s&1) ++t;s>>=1;}return t;
}
int dig[22];//数的每一个数位
/*关于 0 的标记:搜长度为4的时候会搜到 0000搜长度为3的时候会搜到 000但实际上0000和000是同一个数用 zero 记录前一位是不是0
*/
ll dfs(int len,int s,int up,int zero)//当前进行的数位,状态,是否为上界,前一位是否为0
{if (len == 0) return getlen(s) == K;if (!up&&dp[len][s][K]!=-1) return dp[len][s][K];ll ans = 0;int n = 9;if (up) n = dig[len];for (int i = 0;i <= n;++i){if (i == 0){/*0 不能为首位 否则会重复计算当前面有数字的时候就可以加入0*/if (zero) ans += dfs(len-1,0,up&&(i==n),zero&&(i==0));else ans += dfs(len-1,turn(s,i),up&&(i==n),zero&&(i==0));}else ans += dfs(len-1,turn(s,i),up&&(i==n),zero&&(i==0));}if (!up) dp[len][s][K] = ans;return ans;
}
ll cal(ll x)
{int len = 0;while (x){dig[++len] = x%10;x/=10;}return dfs(len,0,1,1);
}
int main()
{int T;scanf("%d",&T);int kas = 0;mem(dp,-1);while (T--){ll l,r;scanf("%I64d %I64d %d",&l,&r,&K);printf("Case #%d: %I64d\n",++kas,cal(r)-cal(l-1));}return 0;
}


这篇关于HDU 4352 XHXJ's LIS(数位DP+状压)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/908624

相关文章

hdu4826(三维DP)

这是一个百度之星的资格赛第四题 题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1004&cid=500 题意:从左上角的点到右上角的点,每个点只能走一遍,走的方向有三个:向上,向下,向右,求最大值。 咋一看像搜索题,先暴搜,TLE,然后剪枝,还是TLE.然后我就改方法,用DP来做,这题和普通dp相比,多个个向上

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

hdu4865(概率DP)

题意:已知前一天和今天的天气概率,某天的天气概率和叶子的潮湿程度的概率,n天叶子的湿度,求n天最有可能的天气情况。 思路:概率DP,dp[i][j]表示第i天天气为j的概率,状态转移如下:dp[i][j] = max(dp[i][j, dp[i-1][k]*table2[k][j]*table1[j][col] )  代码如下: #include <stdio.h>#include

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

usaco 1.1 Broken Necklace(DP)

直接上代码 接触的第一道dp ps.大概的思路就是 先从左往右用一个数组在每个点记下蓝或黑的个数 再从右到左算一遍 最后取出最大的即可 核心语句在于: 如果 str[i] = 'r'  ,   rl[i]=rl[i-1]+1, bl[i]=0 如果 str[i] = 'b' ,  bl[i]=bl[i-1]+1, rl[i]=0 如果 str[i] = 'w',  bl[i]=b

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

hdu 2093 考试排名(sscanf)

模拟题。 直接从教程里拉解析。 因为表格里的数据格式不统一。有时候有"()",有时候又没有。而它也不会给我们提示。 这种情况下,就只能它它们统一看作字符串来处理了。现在就请出我们的主角sscanf()! sscanf 语法: #include int sscanf( const char *buffer, const char *format, ... ); 函数sscanf()和

hdu 2602 and poj 3624(01背包)

01背包的模板题。 hdu2602代码: #include<stdio.h>#include<string.h>const int MaxN = 1001;int max(int a, int b){return a > b ? a : b;}int w[MaxN];int v[MaxN];int dp[MaxN];int main(){int T;int N, V;s

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while