HDU 2089 不要62:

题目大意是给你一个区间,让你统计这个区间里不包含 4 和 62 的数字的个数。

最朴素的思路是:

对于每个区间 [l, r],遍历所有在区间 [l, r] 里的数字,然后检查每个数字是不是合法(没有 4 和 62 ),如果合法答案加一。

代码如下:

#include<bits/stdc++.h>
using namespace std;
bool check(int a) {while (a) {int b = a % 10;int c = a % 100;if (b == 4 || c == 62)return false;a /= 10;}return true;
}
int main() {int n, m;while (cin >> n >> m) {int sum = 0;if (n == 0 && m == 0)return 0;for (int i = n; i <= m; i++) {if (check(i))sum++;}cout << sum << endl;}return 0;
}

对于本题,上面的代码是可以通过的( 998ms 险过),本题时限 1000ms,时间复杂度是非常高的。

引入数位的概念(个位、十位、百位 ... ),有了数位,就可以换一种方式遍历数字,即:

比如对于 [1, 234] 这个区间,就可以按数位去遍历。

遍历百位数,如果百位数小于这一位的最大值 2 ,那么它的的下一位的最大值为 9 ,否则它的下一位最大值就为 3。以此类推,遍历十位数,如果十位数小于 3,那么它的下一位最大值为 9,否则为 4。

对于这种遍历数字的方法,当区间最小值为 1 的时候是容易的,但如果最小值大于 1,遍历的时候不但要考虑数位的最大值,还要考虑数位的最小值,遍历起来会麻烦许多,因此,可以利用前缀和的思想,求 F(n - 1) = { 1 - ( n - 1 ) 的数字数 } 和 F(m) = { 1 - m 的数字数 },然后 ANS = F(m) - F(n - 1)。

那样就可以根据上面的思路写出  统计一个区间里数字个数  的遍历数位式的代码:

统计区间里数字个数(错误):

#include <bits/stdc++.h>
using namespace std;
int a[1005];
int cnt(int po, int lim)
//po表示当前遍历到第几个数位,lim表示本次遍历有没有最大值限制
{if(po == -1)return 1;//如果遍历到最后一位了,返回 1,表示加了一个数字。int len = lim ? a[po] : 9, i, ans = 0;//如果有数位最大值限制,那 len 就是最大值,否则数字可以遍历到 9。for(i = 0; i <= len; i++){ans += cnt(po - 1, i == len ? 1 : 0);//遍历下一位,加到ans里,如果 i 遍历到了最大值,那么下一位将受到限制。}return ans;返回答案。
}
int num(int n)
{int p = 0, i;while(n){a[p++] = n % 10;n /= 10;}//将数字拆分int ans = cnt(p - 1, 1);return ans;
}
int main()
{int n, m;while(scanf("%d %d", &n, &m) != EOF){int ans = num(m) - num(n - 1);//前缀和求解答案。printf("%d\n", ans);}return 0;
}

尽管能很容易的按照思路敲出代码,但上面的代码是错误的!原因就是:

如果某一位不受限制,当这一位遍历到 9 时,它的下一位会变成受限制的一位(不应该受限制)。

那么怎么处理呢?

判断下一位是否受限制的时候要判断当前位是否受限,如果当前位受限且遍历到了最大值,下一位才会受限。

统计区间里数字个数(正确):

#include <bits/stdc++.h>
using namespace std;
int a[1005];
int cnt(int po, int lim)
{if(po == -1)return 1;int len = lim ? a[po] : 9, i, ans = 0;for(i = 0; i <= len; i++){ans += cnt(po - 1, i == len && lim);}return ans;
}
int num(int n)
{int p = 0, i;while(n){a[p++] = n % 10;n /= 10;}int ans = cnt(p - 1, 1);
}
int main()
{int n, m;while(scanf("%d %d", &n, &m) != EOF){int ans = num(m) - num(n - 1);printf("%d\n", ans);}return 0;
}

现在,有了一个用数位方法遍历区间内所有数字的代码,接下来就可以用它来解决问题。

先解决简单的,把包含 4 的数字从答案中删去。

只需要遍历的时候判断一下,遍历到四即跳过。

统计区间里不包含 4 的数字个数:

#include <bits/stdc++.h>
using namespace std;
int a[1005];
int cnt(int po, int lim)
{if(po == -1)return 1;int len = lim ? a[po] : 9, i, ans = 0;for(i = 0; i <= len; i++){if(i == 4)continue;//如果遍历到 4 ,跳过。ans += cnt(po - 1, i == len ? && lim);}return ans;
}
int num(int n)
{int p = 0, i;while(n){a[p++] = n % 10;n /= 10;}int ans = cnt(p - 1, 1);return ans;
}
int main()
{int n, m;while(scanf("%d %d", &n, &m) != EOF){int ans = num(m) - num(n - 1);printf("%d\n", ans);}return 0;
}

做完这些,应该能比较容易的实现 统计一个区间里不包含某个数的数字个数了。

但如果要统计不包含连续的两个数的数字( 62 )该怎么统计呢?

每次遍历到 2 的时候就需要回去看一下它的前一位是否为 6,如果为 6,那么这个数字就不合法。

统计区间里不包含4和62的数字个数:

#include <bits/stdc++.h>
using namespace std;
int a[1005];
int cnt(int po, int lim, int la)
//la表示前一位的数值
{if(po == -1)return 1;int len = lim ? a[po] : 9, i, ans = 0;for(i = 0; i <= len; i++){if(i == 4)continue;if(la == 6 && i == 2){continue;}//如果前一位为 6 且当前位是 2 ,跳过。ans += cnt(po - 1, i == len && lim, i);}return ans;
}
int num(int n)
{int p = 0, i;while(n){a[p++] = n % 10;n /= 10;}int ans = cnt(p - 1, 1, 0);
}
int main()
{int n, m;while(scanf("%d %d", &n, &m) != EOF){int ans = num(m) - num(n - 1);printf("%d\n", ans);}return 0;
}

写到这里,就已经完成了用遍历数位的方法求解这道题目,并且这份代码是可以 AC 的( 187ms ),时间效率上提高了很多。

HDU 3555 Bomb:

题目大意是统计一个区间里包含49的数字个数。

与上面的题目非常相似,从上一题的思路过渡一下:

可以统计区间里不包含 49 的数字个数,然后用区间里数字总个数减去不包含 49 的数字个数,就是要的答案。

拿过上面的代码,稍作修改,就可以得到本题的代码:

统计区间里包含49的数字个数(TLE):

#include <bits/stdc++.h>
using namespace std;
int a[1005];
int cnt(int po, int lim, int la)
{if(po == -1)return 1;int len = lim ? a[po] : 9, i, ans = 0;for(i = 0; i <= len; i++){if(la == 4 && i == 9){continue;}ans += cnt(po - 1, i == len && lim, i);}return ans;
}
int num(int n)
{int p = 0, i;while(n){a[p++] = n % 10;n /= 10;}int ans = cnt(p - 1, 1, 0);
}
int main()
{int n, m;scanf("%d", &m);while(scanf("%d", &n) != EOF){if(n == 0 && m == 0)break;int ans = num(n) - num(0);printf("%d\n", n - ans);}return 0;
}

提交之后发现超时了:

在这份代码中,对于每个数位,其实遍历了不止一次,这样就造成了时间上的浪费,那么可以通过记忆化搜索优化。

统计区间里包含 49 的数字个数(AC):

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[1005], dp[1005][2][10] = {0};
ll cnt(ll po, ll lim, ll la)
{if(po == -1)return 1;if(dp[po][lim][la])return dp[po][lim][la];ll len = lim ? a[po] : 9, i, ans = 0;for(i = 0; i <= len; i++){if(la == 4 && i == 9){continue;}ans += cnt(po - 1, (i == len) && lim, i);}dp[po][lim][la] = ans;return ans;
}
ll num(ll n)
{ll p = 0, i;while(n){a[p++] = n % 10;n /= 10;}memset(dp, 0, sizeof(dp));ll ans = cnt(p - 1, 1, 0);return ans;
}
int main()
{ll n, m;scanf("%lld", &m);while(scanf("%lld", &n) != EOF){//if(n == 0 && m == 0)break;ll ans = num(n) - num(0);printf("%lld\n", n - ans);}return 0;
}

这样的代码大概就是 数位DP 了。

数位DP 学习笔记1(数位DP入门)相关推荐

  1. Bootstrap学习笔记01【快速入门、栅格布局】

    Java后端 学习路线 笔记汇总表[黑马程序员] Bootstrap学习笔记01[快速入门.栅格布局][day01] Bootstrap学习笔记02[全局CSS样式.组件和插件.案例_黑马旅游网][d ...

  2. Flowable学习笔记(一、入门)

    转载自  Flowable学习笔记(一.入门) 一.Flowable简介 1.Flowable是什么 Flowable是一个使用Java编写的轻量级业务流程引擎.Flowable流程引擎可用于部署BP ...

  3. 前端学习笔记:Bootstrap框架入门

    前端学习笔记:Bootstrap框架入门 一.Bootstrap概述 1.基本信息 ​Bootstrap,来自 Twitter,是目前很受欢迎的前端框架.Bootstrap 是基于 HTML.CSS. ...

  4. 学习笔记之Qt从入门到精通(三)

    整理日期: 2010年4月9日 本文是学习笔记之Qt从入门到精通(二)的接续 Part 3: 进阶学习 Qt4 学习笔记 Qt 可以运行在不同的平台,像是Unix/X11.Windows.Mac OS ...

  5. Adams学习笔记 第二讲 Adams入门示例

    Adams学习笔记 第二讲 Adams入门示例 Falling Stone石头坠落 问题描述 使用步骤 结论与分析 软件环境:Adams2018(官方文档使用的开发环境为2013.2) 声明:本讲内容 ...

  6. Vue 新手学习笔记:vue-element-admin 之入门开发教程(v4.0.0 之前)

    说实话都是逼出来的,对于前端没干过ES6都不会的人,vue视频也就看了基础的一些 但没办法,接下来做微服务架构,前端就用 vue,这块你负责....说多了都是泪,脚手架框架布了都没看过 干就完事,不过 ...

  7. 安卓学习笔记(一)入门篇

    总结<第一行代码>Android学习笔记(一)入门篇 1.入门 Android的四层架构和四大组件 运行第一个Android程序 日志工具Log AndroidStudio快捷键 GIT的 ...

  8. 数位DP学习整理(数位DP看完这篇你就会了)

    文章目录 数位DP 数位DP介绍 数位DP解法 数位DP经典例题 例题1:度的数量 例题2:计数问题 例题3:数字游戏 例题4:windy数 例题5:数字游戏Ⅱ 例题6:不要62 例题7:恨7不成妻 ...

  9. 树形$dp$学习笔记

    今天学习了树形\(dp\),一开始浏览各大\(blog\),发现都\(TM\)是题,连个入门的\(blog\)都没有,体验极差.所以我立志要写一篇可以让初学树形\(dp\)的童鞋快速入门. 树形\(d ...

最新文章

  1. Javascript的this用法
  2. 学python用什么书-python有什么好的书籍
  3. Hyper-V P2V转换遇到的问题
  4. hdu1358 最小循环节,最大循环次数 KMP
  5. Android中通过数组资源文件xml与适配器两种方式给ListView列表视图设置数据源
  6. CSDN写博客提交后的网络交互
  7. 较简单的字节输入流输出流拷贝文件
  8. C#编程:SqlCommand.Parameters.Add()方法的参数问题。
  9. Java实战项目,附带源码+视频教程,收藏!
  10. 颜色是如何混合出来的
  11. 全球软件公司排名2020
  12. 24考研数学复习方法、全年规划
  13. kali使用Aircrack-ng破解WEP、WPA的WIFI密码教程
  14. blp和biba属于哪种访问控制_CISP官方信息安全技术章节练习二
  15. android获取手机IMSI号
  16. 数据查询(sql运算符)
  17. 谈谈网络工程师的就业方向与薪资水平
  18. Byte和Integer互转
  19. Visual C++不小心卸载,很多软件不能用了怎么办
  20. 越狱插件找不到依赖包_iphone如何卸载插件依赖包 iphone卸载插件依赖包方法

热门文章

  1. 一个http请求的详细过程
  2. 金庸群侠传3D重制版
  3. 微信小程序中的添加收获地址的流程
  4. 2016年linux认证考试,2016年Linux认证考试复习要点辅导
  5. 每日词根——sol(完整可靠安慰,太阳严肃)
  6. 如何生成Patch及打patch
  7. 设计模式第10式:状态模式
  8. web爬虫学习(四)——手机APP爬取
  9. php相同数据合并单元格,jQuery_基于jQuery的合并表格中相同文本的相邻单元格的代码,ONE 已经生成的数据表格大致 - phpStudy...
  10. C# WPF MVVM框架搭建