转载自: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小记的更多相关文章

  1. 【BZOJ1662】[Usaco2006 Nov]Round Numbers 圆环数 数位DP

    [BZOJ1662][Usaco2006 Nov]Round Numbers 圆环数 Description 正如你所知,奶牛们没有手指以至于不能玩"石头剪刀布"来任意地决定例如谁 ...

  2. bzoj1026数位dp

    基础的数位dp 但是ce了一发,(abs难道不是cmath里的吗?改成bits/stdc++.h就过了) #include <bits/stdc++.h> using namespace ...

  3. uva12063数位dp

    辣鸡军训毁我青春!!! 因为在军训,导致很长时间都只能看书yy题目,而不能溜到机房鏼题 于是在猫大的帮助下我发现这道习题是数位dp 然后想起之前讲dp的时候一直在补作业所以没怎么写,然后就试了试 果然 ...

  4. HDU2089 不要62[数位DP]

    不要62 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  5. 数位DP GYM 100827 E Hill Number

    题目链接 题意:判断小于n的数字中,数位从高到低成上升再下降的趋势的数字的个数 分析:简单的数位DP,保存前一位的数字,注意临界点的处理,都是套路. #include <bits/stdc++. ...

  6. 数位dp总结

    由简单到稍微难点. 从网上搜了10到数位dp的题目,有几道还是很难想到的,前几道基本都是模板题,供入门用. 点开即可看题解. hdu3555 Bomb hdu3652 B-number hdu2089 ...

  7. 数位DP入门

    HDU 2089 不要62 DESC: 问l, r范围内的没有4和相邻62的数有多少个. #include <stdio.h> #include <string.h> #inc ...

  8. 数位DP之奥义

    恩是的没错数位DP的奥义就是一个简练的dfs模板 int dfs(int position, int condition, bool boundary) { ) return (condition ? ...

  9. 浅谈数位DP

    在了解数位dp之前,先来看一个问题: 例1.求a~b中不包含49的数的个数. 0 < a.b < 2*10^9 注意到n的数据范围非常大,暴力求解是不可能的,考虑dp,如果直接记录下数字, ...

随机推荐

  1. windows server2008 r2修改远程桌面连接端口。

    1. windows 2008远程桌面端口默认是用的是3389端口,但是由于安全考虑,通常我们安装好系统后一般都会考虑把原来的3389端口更改为另外的端口.   2.更改过程: 2-1.打开注册表:  ...

  2. 微信 php 获取ticket

    <?phpheader('content-type:text/html; charset=utf8');define('TOKEN', 'youtoken'); // TOKENdefine(' ...

  3. hdu 4619 Warm up 2 二分图匹配

    题目链接 给两种长方形, 水平的和垂直的, 大小都为1*2, n个水平的, m个垂直的, 给出它们的坐标. 水平的和垂直的可以相互覆盖, 但是同种类型的没有覆盖. 去掉一些长方形, 使得剩下的全部都没 ...

  4. codeforces 282E. Sausage Maximization Trie

    题目链接 给n个数, 让你找出一个前缀和一个后缀, 它们异或完以后最大, 前缀和后缀都是异或得来的, 可以为空, 为空时按0算.前缀后缀不可覆盖. 这题好神, 竟然是Trie树... 首先将所有数的异 ...

  5. [LeetCode]题解(python):084-Largest Rectangle in Histogram

    题目来源: https://leetcode.com/problems/largest-rectangle-in-histogram/ 题意分析: 给定一个数组,数组的数字代表这个位置上的bar的高度 ...

  6. 基于SIM 卡卡基不同制作工艺的研究

    1 国内外现行的SIM 卡卡基制作工艺 SIM 卡由卡基和芯片两部分组成.卡基上有植入芯片的台阶式芯片槽,SIM 卡的芯片通过多点焊接植入台阶式芯片槽之中与卡基组成SIM 卡,然后经过个性化数据处理, ...

  7. MVC模式已死

    MVC模式:Model模型 View试图 Control控制器,是目前主流模式,被当作服务器软件入门基本模式学习和掌握,主流框架Struts 1/2 JSF Wicket基本都顺理成章支持MVC模式. ...

  8. 基于Visual C++2013拆解世界五百强面试题--题4-double转换成字符串

    请用C语言实现将double类型数据转换成字符串,再转换成double类型的数据.int类型的数据 想要完成题目中的功能,首先我们的先对系统存储double的格式有所了解. 浮点数编码转换使用的是IE ...

  9. Oracle 表空间操作

    -- 查询已有表空间 SELECT TABLE_SPACENAME FROM DBA_TABLESPACES; -- 创建表空间 CREATE TABLESPACE SPACE DATAFILE ‘E ...

  10. Maven模块聚合与继承

    聚合 假如有account-email和account-persist两个模块,我们想要一次构建这两个项目,这时须要用到聚合. 聚合模块 package值必须为pom 必须有元素modules mod ...