10.19
来源:互联网 发布:zebra标签打印软件 编辑:程序博客网 时间:2024/06/06 00:15
最近在学数位dp
例1.求a~b中不包含49的数的个数. 0 < a、b < 2*10^9
注意到n的数据范围非常大,暴力求解是不可能的,考虑dp,如果直接记录下数字,数组会开不起,要用到数位dp.
数位dp一般应用于:
求出在给定区间[A,B]内,符合条件P(i)的数i的个数.
条件P(i)一般与数的大小无关,而与 数的组成 有关.
这样,我们就要考虑一些特殊的记录方法来做这道题.一般来说,要保存给定数的每个位置的数.然后要记录的状态为当前操作数的位数,剩下的就是根据题目的需要来记录.可以发现,数位dp的题做法一般都差不多,只是定义状态的不同罢了.
下面开始针对例题进行分析:
我们要求[a,b]不包含49的数的个数,可以想到利用前缀和来做,具体来说,就是[a,b] = [0,b] - [0,a)),我们先求出给定a,b的每个位置的数,保存在数组s中,例如a = 109,那么a[1] = 9,a[2] = 0,a[3] = 1.然后开始dp,我们可以选择记忆化搜索或者是递推,记忆化搜索相对于第二种而言简单和较为容易理解一些,所以我们选择记忆化搜索.那么需要记录些什么呢?首先长度是一定要记录的,然后记录当前的数位是否为4,这样就便于在记忆化搜索中得到答案.
然后进行记忆化搜索,记录上一位是否为4和枚举这一位,如果没有限制的话很好办,直接枚举就可以了,但是这样可能会超空间,因此我们每次都必须要判断是否有最大的限制,代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int a, b, shu[20], dp[20][2];
int dfs(int len, bool if4, bool shangxian)
{
if (len == 0)
return 1;
if (!shangxian && dp[len][if4]) //为什么要返回呢?可以画图理解当我们搜到3XXX时,程序运行到1XXX时就已经把3XXX之后的搜索完了,记忆化也是这个用意.
return dp[len][if4];
int cnt = 0, maxx = (shangxian ? shu[len] : 9);
for (int i = 0; i <= maxx; i++)
{
if (if4 && i == 9)
continue;
cnt += dfs(len - 1, i == 4, shangxian && i == maxx); //只有之前有限制现在的达到了上限才能构成限制
}
return shangxian ? cnt : dp[len][if4] = cnt; //如果有限制,那么就不能记忆化,否则记忆的是个错误的数.
}
int solve(int x)
{
memset(shu, 0, sizeof(shu));
int k = 0;
while (x)
{
shu[++k] = x % 10; //保存a,b的数
x /= 10;
}
return dfs(k, false, true);
}
int main()
{
scanf("%d%d", &a, &b);
printf("%d\n", solve(b) - solve(a - 1));
//while (1);
return 0;
}
再来看一道题:例题2.求a~b中不包含62和4的数的个数. 0 < a、b < 2*10^9
分析:和上一题一样,只需要再判断一下4是否出现和上一位是否为6即可.
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int a, b,shu[20],dp[20][2];
int dfs(int len, bool if6, bool shangxian)
{
if (len == 0)
return 1;
if (!shangxian && dp[len][if6])
return dp[len][if6];
int cnt = 0, maxx = (shangxian ? shu[len] : 9);
for (int i = 0; i <= maxx; i++)
{
if (i == 4 || if6 && i == 2)
continue;
cnt += dfs(len - 1, i == 6, shangxian && i == maxx);
}
return shangxian ? cnt : dp[len][if6] = cnt;
}
int solve(int x)
{
memset(shu, 0, sizeof(shu));
int k = 0;
while (x)
{
shu[++k] = x % 10;
x /= 10;
}
return dfs(k, false, true);
}
int main()
{
scanf("%d%d", &a, &b);
printf("%d\n", solve(b) - solve(a - 1));
return 0;
}
数位dp刚刚入门。多看多练~
例1.求a~b中不包含49的数的个数. 0 < a、b < 2*10^9
注意到n的数据范围非常大,暴力求解是不可能的,考虑dp,如果直接记录下数字,数组会开不起,要用到数位dp.
数位dp一般应用于:
求出在给定区间[A,B]内,符合条件P(i)的数i的个数.
条件P(i)一般与数的大小无关,而与 数的组成 有关.
这样,我们就要考虑一些特殊的记录方法来做这道题.一般来说,要保存给定数的每个位置的数.然后要记录的状态为当前操作数的位数,剩下的就是根据题目的需要来记录.可以发现,数位dp的题做法一般都差不多,只是定义状态的不同罢了.
下面开始针对例题进行分析:
我们要求[a,b]不包含49的数的个数,可以想到利用前缀和来做,具体来说,就是[a,b] = [0,b] - [0,a)),我们先求出给定a,b的每个位置的数,保存在数组s中,例如a = 109,那么a[1] = 9,a[2] = 0,a[3] = 1.然后开始dp,我们可以选择记忆化搜索或者是递推,记忆化搜索相对于第二种而言简单和较为容易理解一些,所以我们选择记忆化搜索.那么需要记录些什么呢?首先长度是一定要记录的,然后记录当前的数位是否为4,这样就便于在记忆化搜索中得到答案.
然后进行记忆化搜索,记录上一位是否为4和枚举这一位,如果没有限制的话很好办,直接枚举就可以了,但是这样可能会超空间,因此我们每次都必须要判断是否有最大的限制,代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int a, b, shu[20], dp[20][2];
int dfs(int len, bool if4, bool shangxian)
{
if (len == 0)
return 1;
if (!shangxian && dp[len][if4]) //为什么要返回呢?可以画图理解当我们搜到3XXX时,程序运行到1XXX时就已经把3XXX之后的搜索完了,记忆化也是这个用意.
return dp[len][if4];
int cnt = 0, maxx = (shangxian ? shu[len] : 9);
for (int i = 0; i <= maxx; i++)
{
if (if4 && i == 9)
continue;
cnt += dfs(len - 1, i == 4, shangxian && i == maxx); //只有之前有限制现在的达到了上限才能构成限制
}
return shangxian ? cnt : dp[len][if4] = cnt; //如果有限制,那么就不能记忆化,否则记忆的是个错误的数.
}
int solve(int x)
{
memset(shu, 0, sizeof(shu));
int k = 0;
while (x)
{
shu[++k] = x % 10; //保存a,b的数
x /= 10;
}
return dfs(k, false, true);
}
int main()
{
scanf("%d%d", &a, &b);
printf("%d\n", solve(b) - solve(a - 1));
//while (1);
return 0;
}
再来看一道题:例题2.求a~b中不包含62和4的数的个数. 0 < a、b < 2*10^9
分析:和上一题一样,只需要再判断一下4是否出现和上一位是否为6即可.
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int a, b,shu[20],dp[20][2];
int dfs(int len, bool if6, bool shangxian)
{
if (len == 0)
return 1;
if (!shangxian && dp[len][if6])
return dp[len][if6];
int cnt = 0, maxx = (shangxian ? shu[len] : 9);
for (int i = 0; i <= maxx; i++)
{
if (i == 4 || if6 && i == 2)
continue;
cnt += dfs(len - 1, i == 6, shangxian && i == maxx);
}
return shangxian ? cnt : dp[len][if6] = cnt;
}
int solve(int x)
{
memset(shu, 0, sizeof(shu));
int k = 0;
while (x)
{
shu[++k] = x % 10;
x /= 10;
}
return dfs(k, false, true);
}
int main()
{
scanf("%d%d", &a, &b);
printf("%d\n", solve(b) - solve(a - 1));
return 0;
}
数位dp刚刚入门。多看多练~
阅读全文
0 0
- 10.19
- 10.19
- 10.19
- 10.19
- 10.19
- 10.19
- 10.19
- 10.19
- 10.19
- 10.19
- 10.19
- 10.19
- 10.19囧太鼓
- 习题10.19
- 笔试10.19
- 10.19周四
- 10.19 信号_sleep函数
- 习题10.18、10.19
- CentOS6/7防火墙设置
- C程序,简单模仿扫雷游戏,10行 10列
- Canvas的基本使用——常用的Api方法(2):clip*裁剪方法
- 我的读书笔记——Serial收集器
- CS231n Convolutional Neural Networks for Visual Recognition
- 10.19
- python运算符
- 我的读书笔记——parNew收集器
- unbutu上my.cnf权限问题
- 工作一年多对于前后端联调些许记录
- Maven补充
- 我的读书笔记——Paralled Scavenge 收集器
- 信号量通信
- the difference of real and fake fingerprint