hdu2089 数位dp

数位dp第一题

题意:找从n到m中,不包含4,62(连续)的个数

先预处理出f数组     f[i][j]是以j开头的i位数不包含4,62的个数,可通过递推求解

递推关系式为:  f[i][j]=f[i-1][k]+f[i][j];对于第i位,可由第i-1位转移过来,用k枚举第i-1位的所有情况,j枚举第i位的所有情况,这时只需判断i和i-1位不为62,i位不为2的情况

用digit数组来记录从右到左的第i位,然后从最高位(最左边)开始枚举,从0枚举到digit【i】

例如432,这样便列举了所有情况

  4 3 2 4 3 2 4 3 2
枚举 0 枚举到3的取值 枚举到2的取值 4 0 枚举到2的取值 4 3 0
  1 枚举到3的取值 枚举到2的取值 4 1 枚举到2的取值 4 3 1
  2 枚举到3的取值 枚举到2的取值 4 2 枚举到2的取值      
  3 枚举到3的取值 枚举到2的取值            
                   
                   
                   
                   
                   

如果枚举完之后,保持最大值不变的情况下出现了4,62,则退出,如6231(枚举到3或1的情况时62会保持出现,这种情况要删去)

非递归版本的

#include<bits/stdc++.h>
#define C 0.5772156649
#define pi acos(-1.0)
#define ll long long
#define mod 1000000007
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;

const double g=10.0,eps=1e-7;
const int N=10+10,maxn=1000000+10,inf=0x3f3f3f;

int f[N][N];//f[i][j]以j开头的i位数不包含4,62的个数
int digit[10];//从右到左第i位是多少
int getnum(int x)
{
    int len=0,ans=0;
    memset(digit,0,sizeof digit);
    while(x)
    {
        digit[++len]=x%10;
        x/=10;
    }
    for(int i=len; i>=1; i--)
    {
        for(int j=0; j<digit[i]; j++)
            if(j!=4&&(j!=2||digit[i+1]!=6))
                ans+=f[i][j];
        if(digit[i]==4||(digit[i]==2&&digit[i+1]==6))break;
    }
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    f[0][0]=1;
    for(int i=1;i<=7;i++)
        for(int j=0;j<=9;j++)//第i位
            for(int k=0;k<=9;k++)//第i-1位
                if(j!=4&&(j!=6||k!=2))
                    f[i][j]=f[i-1][k]+f[i][j];
    int n,m;
    while(cin>>n>>m)
    {
        if(!n&&!m)break;
        cout<<getnum(m+1)-getnum(n)<<endl;
    }
    return 0;
}
/********************

********************/
View Code

 递归版本的

#include<bits/stdc++.h>
#define C 0.5772156649
#define pi acos(-1.0)
#define ll long long
#define mod 1000000007
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
#pragma comment(linker, "/STACK:1024000000,1024000000")

using namespace std;

const double g=10.0,eps=1e-7;
const int N=20+10,maxn=1000000+10,inf=0x3f3f3f;

int dp[N][N],digit[N];
int dfs(int len,bool state,bool fp)//state-当前是不是等于6,fp是否访问过
{
    if(!len)return 1;
    if(!fp&&dp[len][state]!=-1)return dp[len][state];
    int ret=0,fpmax=fp ? digit[len] : 9;
    for(int i=0;i<=fpmax;i++)
    {
        if(i==4||state&&i==2)continue;
        ret+=dfs(len-1,i==6,fp&&i==fpmax);
    }
    if(!fp)dp[len][state]=ret;
    return ret;
}
int solve(int x)
{
    int len=0;
    while(x)
    {
        digit[++len]=x%10;
        x/=10;
    }
    return dfs(len,0,1);
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int a,b;
    memset(dp,-1,sizeof dp);
    while(cin>>a>>b)
    {
        if(a==0&&b==0)break;
        cout<<solve(b)-solve(a-1)<<endl;
    }
    return 0;
}
/********************

********************/
View Code

发现网上的代码关于数位dp的几乎都是递归写的,所以还是找递归版本理解比较好

posted @ 2017-08-23 10:44  walfy  阅读(151)  评论(0编辑  收藏  举报