关于数位dp

来源:互联网 发布:文档网络是什么 编辑:程序博客网 时间:2024/06/08 07:58

"在信息学竞赛中,有一类与数位有关的区间统计问题。这类问题往往具有比较浓厚的数学味道,无法暴力求解,需要在数位上进行递推等操作。"

——刘聪《浅谈数位类统计问题》

这类问题往往需要一些预处理,这就用到了数位dp

题目中常需要统计区间[l ,r]的满足题意的数的个数,这往往可以转化成求 [0, r+1) - [0, l )。


对于求区间[ 0, n)有一个通用的方法:

对于一个小于n的数,肯定是从高位到低位出现某一位<n的那一位。

比如n=58  n为十进制数。

      x=49,十位<n;  x=51, 个位<n;

根据该性质,我们可以从高到底枚举第一次<n对应的是哪一位,这样之前的位已确定,之后的位不受n的限制,可以从0~99...9,这部分可以先预处理,然后直接统计答案。

预处理f 数组:

f [ i, st ]表示位数为i(可能允许前导0),状态为st 的方案数。这里的st根据题目需要而定。

决策第i位是多少,

f[i,st]=f[i,st]+f[i-1,st'],st‘为相应的状态


例题:

hdu2089:给定区间[n,m],求在n到m中没有“62”或“4”的数的个数。如62315包含62,88914包含4,这两个数都不合法。0<n<=m<1000000

分析:预处理f 数组:f [i,j]表示首位为j的i位数中不含“62”或“4”的数的个数。

                                f[i,st]=f[i,st]+f[i-1,st'],st‘为相应的状态,就该题,下一位的相应状态为:下一位不为4&&若该位为6,下一位不为2

统计区间[0,n):从高到底枚举哪一位比n小,然后枚举这一位的取值,加上合法的情况。!接下来,要判断已经确定的位数是否出现4或62,如果是,则直接结束枚举。因为之后的位数无论怎样取,该数都不合法。

代码:

#include<iostream>#include<cstdio>#include<cstdlib>#include<algorithm>#include<cmath>#include<cstring>#include<stack>#include<queue>#include<map>#include<set>using namespace std;int f[10][10];int cal(int n){    int len=0,num[10],sum=0,i,j;    num[0]=0;    while(n)    {        num[len]=n%10;        len++;        n/=10;    }    num[len]=0;    for(i=len-1;i>=0;i--)//从高位到低位枚举    {        for(j=0;j<num[i];j++)        {            if(j!=4&&!(j==2&&num[i+1]==6))                sum+=f[i+1][j];        }        if(num[i]==4||num[i+1]==6&&num[i]==2)//判断已确定的位数是否合法            break;    }    return sum;}int main(){    int n,m,i,j,k;    memset(f,0,sizeof(f));    f[0][0]=1;    for(i=1;i<=7;i++)    {        for(j=0;j<=9;j++)        {            for(k=0;k<=9;k++)            {                if(j!=4&&!(j==6&&k==2))//判断情况是否合法                    f[i][j]+=f[i-1][k];            }        }    }    while(scanf("%d %d",&n,&m)&&(n||m))    {        int sum=cal(m+1)-cal(n);        printf("%d\n",sum);    }    return 0;}

未完待续.....

0 0
原创粉丝点击