第一次接触数位dp的问题,在这里记录一下我个人的学习思路,如果能够对大家有什么帮助那就是最好的了。
动态规划也是刚刚开始学习,说实话对这种类型的题目现在还没有太适应。。。
数位dp通常都是类似这种叙述的问题:给定区间[l , m],求解其中满足(不满足)条件 p 的解集大小。
初始化的时候会开一个数组dp[n][10],dp[i][j] 表示第 i 位上为数字 j 并且符合(不符合)条件 p 的数目。一般用一个三重循环来搞定初始化的数值:
(pseudo code is as follow)
dp[0][0] = 1;
for (int i = 1; i < n; i++)
{
for (int j = 0; j < 10; j++)
{
for (int k = 0; k < 10; k++)
{
if (p)
{
dp[i][j] += dp[i - 1][k];
}
}
}
}
第一重循环是n - 1位数位完全遍历,第二重和第三重循环分别是第 i 位和第 i - 1 位数位上0~9的数值。
dp[i][j] = dp[i][j] + dp[i - 1][k] 的含义是,当前位解集的大小为满足当前条件的数目和上一位解集大小之和(将这个过程联想成一棵树形的迭代结构)。接下来的过程就是根据题目具体描述来进行分析了。
HDU-2089 不要62 问题概述:给定数值范围n,m;求其中不是不吉利的数有多少个(不含4并且不含62)
AC代码如下
#include<iostream>
using namespace std;
//#include<cmath>
//#include<string>
long long dp[10][10];
void init()
{
dp[0][0] = 1;
for (int i = 1; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
for (int k = 0; k < 10; k++)
{
if (j != 4 && !(j == 6 && k == 2))
{
dp[i][j] += dp[i - 1][k];
}
}
}
}
}
long long solution(long long n)
{
int digit[15];
int len = 0;
long long mem = n, ans = 0;
while (n > 0)
{
digit[++len] = n % 10;
n /= 10;
}
digit[len + 1] = 0;
for (int i = len; i > 0; i--)
{
for (int j = 0; j < digit[i]; j++)
{
if (!(digit[i+1] == 6 && j == 2) && j != 4)
{
ans += dp[i][j];
}
}
if ((digit[i] == 2 && digit[i + 1] == 6) || digit[i] == 4)
{
break;
}
}
return ans;
}
int main()
{
init();
long long n, m;
while (cin >> n >> m)
{
if (n == 0 && m == 0)
{
break;
}
cout << solution(m + 1) - solution(n)<< endl;
}
//system("pause");
return 0;
}
相邻两位进行判断即可
HDU-3555 Bomb 问题概述:输入最大倒计时数n,求炸弹能量增加的最大值(求小于n并且含49的数字有多少个)
#include<iostream>
using namespace std;
//#include<cmath>
//#include<string>
long long dp[20][10];
void init()
{
dp[0][0] = 1;
for (int i = 1; i < 20; i++)
{
for (int j = 0; j < 10; j++)
{
for (int k = 0; k < 10; k++)
{
if (!(j == 4 && k == 9))
{
dp[i][j] += dp[i - 1][k];
}
}
}
}
}
long long solution(long long n)
{
int digit[20];
int len = 0;
long long mem = n, ans = 0;
while (n > 0)
{
digit[++len] = n % 10;
n /= 10;
}
digit[len + 1] = 0;
for (int i = len; i > 0; i--)
{
for (int j = 0; j < digit[i]; j++)
{
if (!(digit[i+1] == 4 && j == 9))
{
ans += dp[i][j];
}
}
if ((digit[i] == 9 && digit[i + 1] == 4))
{
break;
}
}
return mem - ans;
}
int main()
{
init();
int t;
long long n;
cin >> t;
while (t --)
{
cin >> n;
cout << solution(n + 1) << endl;
}
//system("pause");
return 0;
}
整体和2089的思路一样,换了个判断条件而已。