[转]数位dp小记
转载自:http://blog.csdn.net/guognib/article/details/25472879
参考:
http://www.cnblogs.com/jffifa/archive/2012/08/17/2644847.html
kuangbin :http://www.cnblogs.com/kuangbin/category/476047.html
http://blog.csdn.net/cmonkey_cfj/article/details/7798809
http://blog.csdn.net/liuqiyao_01/article/details/9109419
数位dp有递推和记忆化搜索的方法,比较来说记忆化搜索方法更好。通过博客一的一个好的模板,数位dp就几乎变成一个线性dp问题了。
下为博客一内容:
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
偷看了下7k+大牛的数位统计dp写法,通常的数位dp可以写成如下形式:
- int dfs(int i, int s, bool e) {
- if (i==-) return s==target_s;
- if (!e && ~f[i][s]) return f[i][s];
- int res = ;
- int u = e?num[i]:;
- for (int d = first?:; d <= u; ++d)
- res += dfs(i-, new_s(s, d), e&&d==u);
- return e?res:f[i][s]=res;
- }
其中:
f为记忆化数组;
i为当前处理串的第i位(权重表示法,也即后面剩下i+1位待填数);
s为之前数字的状态(如果要求后面的数满足什么状态,也可以再记一个目标状态t之类,for的时候枚举下t);
e表示之前的数是否是上界的前缀(即后面的数能否任意填)。
for循环枚举数字时,要注意是否能枚举0,以及0对于状态的影响,有的题目前导0和中间的0是等价的,但有的不是,对于后者可以在dfs时再加一个状态变量z,表示前面是否全部是前导0,也可以看是否是首位,然后外面统计时候枚举一下位数。It depends.
于是关键就在怎么设计状态。当然做多了之后状态一眼就可以瞄出来。
注意:
不满足区间减法性质的话(如hdu 4376),不能用solve(r)-solve(l-1),状态设计会更加诡异。
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
正如上面说的要注意:
前导零是否有影响。
是否满足区间减的性质。(如hdu4376,还没做,先记上)
几乎就是模板题:
模板:
UESTC 1307相邻的数差大于等于2
(不允许有前导0,前导0对计算有影响,注意前导0的处理)
- int dp[][];
- int bit[];
- int dfs(int pos, int s, bool limit, bool fzero)
- {
- if (pos == -) return ;///前导0的影响!!!
- if (!limit && !fzero && ~dp[pos][s]) return dp[pos][s];///条件判断!!!
- int end = limit ? bit[pos] : ;
- int ans = ;
- for (int i = ; i <= end; i++)
- {
- if (!fzero && abs(i - s) < ) continue;///前导0的影响!!!
- int nows = i;
- ans += dfs(pos - , nows, limit && (i == end), fzero && !i);
- }
- return limit || fzero ? ans : dp[pos][s] = ans;///条件判断!!!
- }
- int calc(int n)
- {
- if (n == ) return ;
- int len = ;
- while (n)
- {
- bit[len++] = n % ;
- n /= ;
- }
- return dfs(len - , , , );
- }
- int main ()
- {
- memset(dp, -, sizeof(dp));
- int l, r;
- while (cin >> l >> r)
- {
- cout << calc(r) - calc(l - ) << endl;
- }
- return ;
- }
其它一些题目:
Hdu3555不能出现连续的49
- int bit[];
- LL dp[][];
- /**
- 0
- 1
- 2
- */
- /**
- pos为当前考虑的位置
- s为在pos之前已到达的状态
- limit当前考虑的数字是否有限制,即之前已确定的数是否为n的前缀
- */
- LL dfs(int pos, int s, bool limit)
- {
- if (pos == -) return s == ;
- if (!limit && ~dp[pos][s]) return dp[pos][s];
- int end = limit ? bit[pos] : ;///limit选择
- LL ans = ;
- for (int i = ; i <= end; i++)
- {
- int nows;
- if (s == )
- {
- if (i == ) nows = ;
- else nows = ;
- }
- else if (s == )
- {
- if (i == ) nows = ;
- else if (i == ) nows = ;
- else nows = ;
- }
- else if (s == ) nows = ;
- ans += dfs(pos - , nows, limit && i == end);
- }
- return limit ? ans : dp[pos][s] = ans;///limit选择
- }
- LL calc(LL n)
- {
- ///
- int len = ;
- while (n)
- {
- bit[len++] = n % ;
- n /= ;
- }
- ///
- return dfs(len - , , );
- }
- int main ()
- {
- memset(dp, -, sizeof(dp));
- int T;
- RI(T);
- LL n;
- while (T--)
- {
- cin >> n;
- cout << calc(n) << endl;
- }
- return ;
- }
hdu2089 不要62
- int dp[][];
- int bit[];
- int dfs(int pos, int s, int limit, bool first)
- {
- if (pos == -) return s == ;
- if (!limit && ~dp[pos][s]) return dp[pos][s];
- int end = limit ? bit[pos] : ;
- // int be = first ? 1 : 0;
- int ans = ;
- for (int i = ; i <= end; i++)
- {
- int nows = s;
- if (s == )
- {
- if (i == ) nows = ;
- else if (i == ) nows = ;
- }
- if (s == )
- {
- if (i == || i == ) nows = ;
- else if (i == ) nows = ;
- else nows = ;
- }
- if (i == ) nows = ;
- ans += dfs(pos - , nows, limit && (i == end), first && !i);
- }
- return limit ? ans : dp[pos][s] = ans;
- }
- int calc(int n)
- {
- int tmp = n;
- if (n == ) return ;
- int len = ;
- while (n)
- {
- bit[len++] = n % ;
- n /= ;
- }
- return tmp - dfs(len - , , , );
- }
- int main ()
- {
- memset(dp, -, sizeof(dp));
- int l, r;
- // for (int i = 1; i <= 100; i++)
- // cout << i << ' ' << calc(i) << endl;
- while (cin >> l >> r)
- {
- if (l + r == ) break;
- // cout << calc(r) << ' ' << calc(l - 1) << endl;
- cout << calc(r) - calc(l - ) << endl;
- }
- return ;
- }
UESTC 1307相邻的数差大于等于2
(注意前导0的处理)
- int dp[][];
- int bit[];
- int dfs(int pos, int s, bool limit, bool fzero)
- {
- if (pos == -) return ;///前导0的影响!!!
- if (!limit && !fzero && ~dp[pos][s]) return dp[pos][s];///条件判断!!!
- int end = limit ? bit[pos] : ;
- int ans = ;
- for (int i = ; i <= end; i++)
- {
- if (!fzero && abs(i - s) < ) continue;///前导0的影响!!!
- int nows = i;
- ans += dfs(pos - , nows, limit && (i == end), fzero && !i);
- }
- return limit || fzero ? ans : dp[pos][s] = ans;///条件判断!!!
- }
- int calc(int n)
- {
- if (n == ) return ;
- int len = ;
- while (n)
- {
- bit[len++] = n % ;
- n /= ;
- }
- return dfs(len - , , , );
- }
- int main ()
- {
- memset(dp, -, sizeof(dp));
- int l, r;
- while (cin >> l >> r)
- {
- cout << calc(r) - calc(l - ) << endl;
- }
- return ;
- }
POJ 3252 Round Number (组合数)!!!
拆成2进制,在记录0和1的个数
求区间[l,r]中,满足传化成2进制后,0的个数>=1的个数的,数字的个数
- int dp[][][];
- int bit[];
- int dfs(int pos, int num0, int num1, bool limit, bool fzero)
- {
- if (pos == -) return num0 >= num1;///前导0的影响!!!
- if (!limit && !fzero && ~dp[pos][num0][num1]) return dp[pos][num0][num1];///条件判断!!!
- int end = limit ? bit[pos] : ;
- int ans = ;
- for (int i = ; i <= end; i++)
- {
- int new0, new1;
- if (fzero)
- {
- new0 = , new1 = ;
- if (i) new1 = ;
- }
- else
- {
- new0 = num0, new1 = num1;
- if (i) new1++;
- else new0++;
- }
- ans += dfs(pos - , new0, new1, limit && (i == end), fzero && !i);
- }
- return limit || fzero ? ans : dp[pos][num0][num1] = ans;///条件判断!!!
- }
- int calc(int n)
- {
- if (n == ) return ;
- int len = ;
- while (n)
- {
- bit[len++] = n % ;
- n /= ;
- }
- return dfs(len - , , , , );
- }
- int main ()
- {
- memset(dp, -, sizeof(dp));
- int l, r;
- while (cin >> l >> r)
- {
- cout << calc(r) - calc(l - ) << endl;
- }
- return ;
- }
hdu3886求满足符号串的数字个数!!!
统计满足和指定 升降字符串 匹配的个数
- int dp[][][];
- int bit[];
- char s[];
- char a[], b[];
- int ns;
- bool ok(int r, int a, int b)
- {
- if (s[r] == '/') return a < b;
- else if (s[r] == '-') return a == b;
- else if (s[r] == '\\') return a > b;
- }
- int dfs(int pos, int r, int pre, bool limit, bool prezero)
- {
- if (pos == -) return (r == ns);
- if (!limit && !prezero && ~dp[pos][r][pre]) return dp[pos][r][pre];
- int end = limit ? bit[pos] : ;
- int ans = ;
- for (int i = ; i <= end; i++)
- {
- if (prezero)
- {
- ans += dfs(pos - , r, i, limit && (i == end), prezero && (!i));
- }
- else
- {
- if (r < ns && ok(r, pre, i))///优先考虑向后拓展
- ans += dfs(pos - , r + , i, limit && (i == end), );
- else if (r > && ok(r - , pre, i))
- ans += dfs(pos - , r, i, limit && (i == end), );
- }
- ans %= MOD;
- }
- if (!limit && !prezero) dp[pos][r][pre] = ans;
- return ans;
- }
- int calc(char a[], bool dec)
- {
- int n = strlen(a);
- int len = , tmp = ;
- while (a[tmp] == '') tmp++;
- for (int i = n - ; i >= tmp; i--)
- {
- bit[len++] = a[i] - '';
- }
- if (dec && len > )
- {
- for (int i = ; i < len; i++)
- {
- if (bit[i])
- {
- bit[i]--;
- break;
- }
- else bit[i] = ;
- }
- }
- return dfs(len - , , , , );
- }
- int main ()
- {
- while (scanf("%s", s) != EOF)
- {
- memset(dp, -, sizeof(dp));
- ns = strlen(s);
- scanf("%s%s", a, b);
- printf("%08d\n", (calc(b, ) - calc(a, ) + MOD) % MOD);
- }
- return ;
- }
HDU 3709 平衡数
- LL dp[][][];
- ///力矩最大为不超过10*20*10;
- int bit[];
- LL dfs(int pos, int r, int sum, int e)
- {
- if (pos == -) return sum == ;
- if (sum < ) return ;
- if (!e && ~dp[pos][r][sum]) return dp[pos][r][sum];
- int end = e ? bit[pos] : ;
- LL ans = ;
- for (int i = ; i <= end; i++)
- {
- ans += dfs(pos - , r, sum + i * (pos - r), e && (i == end));
- }
- if (!e) dp[pos][r][sum] = ans;
- return ans;
- }
- LL calc(LL n)
- {
- if (n < ) return ;
- int len = ;
- while (n)
- {
- bit[len++] = n % ;
- n /= ;
- }
- LL ans = ;
- for (int i = ; i < len; i++)///需要枚举支点
- ans += dfs(len - , i, , );
- return ans - (len - );
- }
- int main ()
- {
- memset(dp, -, sizeof(dp));
- int t;
- LL l, r;
- RI(t);
- while (t--)
- {
- scanf("%I64d%I64d", &l, &r);
- printf("%I64d\n", calc(r) - calc(l - ));
- }
- return ;
- }
HDU4352严格上升子序列的长度为K的个数。!!!
最长上升子序列结合,通过集合(1<<10)来处理
- LL dp[][ << ][];
- int bit[];
- int k;
- int getnews(int s, int x)
- {
- for(int i=x;i<;i++)
- if(s&(<<i))return (s^(<<i))|(<<x);
- return s|(<<x);
- }
- int getnum(int s)
- {
- int ret = ;
- while (s)
- {
- if (s & ) ret++;
- s >>= ;
- }
- return ret;
- }
- LL dfs(int pos, int s, int e, int z)
- {
- if (pos == -) return getnum(s) == k;
- if (!e && ~dp[pos][s][k]) return dp[pos][s][k];
- int end = e ? bit[pos] : ;
- LL ans = ;
- for (int i = ; i <= end; i++)
- {
- int news = getnews(s, i);
- if (z && !i) news = ;
- ans += dfs(pos - , news, e && (i == end), z && (!i));
- }
- if (!e) dp[pos][s][k] = ans;
- return ans;
- }
- LL calc(LL n)
- {
- int len = ;
- while (n)
- {
- bit[len++] = n % ;
- n /= ;
- }
- return dfs(len - , , , );
- }
- int main ()
- {
- LL n, m;
- memset(dp, -, sizeof(dp));
- int t;
- scanf("%d", &t);
- int nc = ;
- while (t--)
- {
- cin >> n >> m >> k;
- printf("Case #%d: ", nc++);
- cout << calc(m) - calc(n - ) << endl;
- }
- return ;
- }
!!!是比较不错,待看的题
比较还好的处理的题目:
Codeforces 55D Beautiful numbers!!!
spoj 10606 Balanced Numbers
ac自动机和数位dp结合(!!!):
hdu4376!!!(区间不可减???)
ZOJ3494 BCD Code(AC自动机+数位DP)!!!
整除和简单统计:
HDU4507 和7无关数的平方和!!!
HDU 3652 出现13,而且能被13整除
LightOJ 1068 能被K整数且各位数字之和也能被K整除的数
light OJ 1140两个数之间的所有数中零的个数。
lightoj 1032 二进制数中连续两个‘1’出现次数的和
其它:
LightOJ1205求区间[a,b]的回文数个数。
ural 1057 数位统计
codeforces215E周期数
codeforces258B在1-m中任选7个数,要使前六个数字中的“4”,"7"之和小于第七个的,
Zoj2599 数位统计(见题意)
zoj3162分形、自相似
[转]数位dp小记的更多相关文章
- 【BZOJ1662】[Usaco2006 Nov]Round Numbers 圆环数 数位DP
[BZOJ1662][Usaco2006 Nov]Round Numbers 圆环数 Description 正如你所知,奶牛们没有手指以至于不能玩"石头剪刀布"来任意地决定例如谁 ...
- bzoj1026数位dp
基础的数位dp 但是ce了一发,(abs难道不是cmath里的吗?改成bits/stdc++.h就过了) #include <bits/stdc++.h> using namespace ...
- uva12063数位dp
辣鸡军训毁我青春!!! 因为在军训,导致很长时间都只能看书yy题目,而不能溜到机房鏼题 于是在猫大的帮助下我发现这道习题是数位dp 然后想起之前讲dp的时候一直在补作业所以没怎么写,然后就试了试 果然 ...
- HDU2089 不要62[数位DP]
不要62 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
- 数位DP GYM 100827 E Hill Number
题目链接 题意:判断小于n的数字中,数位从高到低成上升再下降的趋势的数字的个数 分析:简单的数位DP,保存前一位的数字,注意临界点的处理,都是套路. #include <bits/stdc++. ...
- 数位dp总结
由简单到稍微难点. 从网上搜了10到数位dp的题目,有几道还是很难想到的,前几道基本都是模板题,供入门用. 点开即可看题解. hdu3555 Bomb hdu3652 B-number hdu2089 ...
- 数位DP入门
HDU 2089 不要62 DESC: 问l, r范围内的没有4和相邻62的数有多少个. #include <stdio.h> #include <string.h> #inc ...
- 数位DP之奥义
恩是的没错数位DP的奥义就是一个简练的dfs模板 int dfs(int position, int condition, bool boundary) { ) return (condition ? ...
- 浅谈数位DP
在了解数位dp之前,先来看一个问题: 例1.求a~b中不包含49的数的个数. 0 < a.b < 2*10^9 注意到n的数据范围非常大,暴力求解是不可能的,考虑dp,如果直接记录下数字, ...
随机推荐
- [NOIP2001提高组]CODEVS1014 Car的旅行路线(最短路)
最短路,这个不难想,但是要为它加边就有点麻烦..还好写完就过了(虽然WA了一次,因为我调试用的输出没删了..),不然实在是觉得挺难调的.. ------------------------------ ...
- dubbo架构演变之路
背景 (#) 随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进. 单一应用架构 当网站流量很小时, ...
- app微信支付服务器端php demo
class Wxpay { /* 配置参数 */ private $config = array( 'appid' => "wxc92b12277f277355", /*微信 ...
- 利用python进行数据分析之pandas库的应用(二)
本节介绍Series和DataFrame中的数据的基本手段 重新索引 pandas对象的一个重要方法就是reindex,作用是创建一个适应新索引的新对象 >>> from panda ...
- [LeetCode]题解(python):007-Reverse Integer
题目来源: https://leetcode.com/problems/reverse-integer/ 题意分析: 这道题目很简单,就是将一个数反转,123变321,-123变321. 题目思路: ...
- vs2013 cpu占用100%问题
是由于显卡驱动支持wpf有问题 更新驱动或设置里取消自动调节视觉效果 http://support.microsoft.com/kb/2894215
- javascript Node操作
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 一个高效过滤非UTF8字符的C函数(也可用来判断是否utf8)
/* UTF-8 valid format list: 0xxxxxxx 110xxxxx 10xxxxxx 1110xxxx 10xxxxxx 10xxxxxx 11110xxx 10xxxxxx ...
- spring学习总结(mybatis,事务,测试JUnit4,日志log4j&slf4j,定时任务quartz&spring-task,jetty,Restful-jersey等)
在实战中学习,模仿博客园的部分功能.包括用户的注册,登陆:发表新随笔,阅读随笔:发表评论,以及定时任务等.Entity层设计3张表,分别为user表(用户),essay表(随笔)以及comment表( ...
- SPOJ1811最长公共子串问题(后缀自动机)
题目:http://www.spoj.com/problems/LCS/ 题意:给两个串A和B,求这两个串的最长公共子串. 分析:其实本题用后缀数组的DC3已经能很好的解决,这里我们来说说利用后缀自动 ...