我最近看的是一个数位DP,这个类型的我看了好几个,有几个博客都不大好理解,最后才找到一个好理解的。
具体 举个例子:求【L,R】有多少个数里面含有5,或23
数组是一个二维的,第二维存着三个状态
dp[i][0]代表到一个i位数有多少个不存在上述条件的
dp[i][[1]代表一个i位数在不存在上述条件的前提下,最高位是3的个数
dp[i][2]代表一个i位数满足条件的个数
代码如下:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int dp[10][3];
void init()
{
memset(dp, 0, sizeof(dp));
dp[0][0] = 1;
for(int i = 1; i <= 9; i ++)
{
dp[i][0] = dp[i - 1][0] * 9 - dp[i - 1][1]; // 在第一种情况下最高位添加除5之外的数,但是上一位是3,最高位加上了2,就不满足这个条件了
//这个地方我想了好久,因为没有注意是第一种情况包含第二种情况,反正想了好久
dp[i][1] = dp[i - 1][0]; //在第一种情况下,在最高位添加3的个数
dp[i][2] = dp[i - 1][2] * 10 + dp[i - 1][0] + dp[i - 1][1];
//在满足条件的情况下在最高位加10种任意数都满足和在第一种情况下前面加5和第二种条件下加上2
}
}
int solve(int x)
{
int digit[15];
int cnt = 0;
int tmp = x;
while(tmp) // 取出每一位的数,把整数分为各个数位
{
digit[++cnt] = tmp % 10;
tmp /= 10;
}
digit[cnt + 1] = 0; // 为了下面的digit[i + 1],i = cnt 的时候
int flag = 0, ans = 0; // ans 代表符合题意的数的个数
for(int i = cnt; i > 0; i --)
{
ans += digit[i] * dp[i - 1][2];
if(flag) // 当此数满足题意时,比如这个数是 9236
ans += digit[i] * dp[i - 1][0]; // 所以 923之间满足题意的 , 再乘以6 就是所有满足题意的数的个数
else
{
if(digit[i] > 5) // 当你这个位置上的数大于5 的时候,说明你这个数肯定包含了这个位置存在5 的情况
ans += dp[i - 1][0];
if(digit[i] > 2)
ans += dp[i - 1][1];
if(digit[i + 1] == 2 && digit[i] > 3)
ans += dp[i][1];
}
if(digit[i] == 5 || digit[i + 1] == 2 && digit[i] == 3)
flag = 1;
}
return ans;
}
int main()
{
int a, b;
init();
while(~scanf("%d%d",&a,&b))
{
if(a == 0 && b == 0)
break;
printf("%d\n", solve(b + 1) - solve(a)); // 在sovle 函数中,解决没有包括b ,所以要b + 1(这个地方我没有理解,还要再看看)
}
}
(我的理解也在里面了,就是代码粘的别的大佬的http://m.blog.youkuaiyun.com/xi__long/article/details/38091561)