hdu 2089 入手数位dp问题

数位dp解决的问题是指求在一段数的区间里面 满足条件的数的个数

核心为两点 

http://wenku.baidu.com/link?url=tpfIYzhx_MzevpIM58UZ66pr-87MCFPKTMKFdGDi5jUqyO9ckti0mY6diSz2PZEL_ZBhd2zIbhus1mnzDiAO1B5K2Vu38YDsqjmOvYKFT6q

我自己总结下吧

第一 是dp的状态转移方程 

dp[i][j] 表示的是以j开头的i位数 满足条件的个数为多少 

既然是要记录满足条件的个 那么 dp[i][j]=sum(dp[i-1][0~9]) 这个不难理解 

for(int i=1;i<=7;i++)
        {
            for(int j=0;j<=9;j++)
            {
                for(int k=0;k<=9;k++)
                {
                    if(j!=4&&!(j==6&&k==2)) dp[i][j]+=dp[i-1][k];
                }
            }
        }

然后就是solve函数问题 solve(i)这个函数的作用是求出【0,i)区间满足条件的数的个数

大致的思想 是从高位开始 逐一枚举比该为数小的数 在满足的条件的情况下 统计和 每次枚举完

举完一位以后 由于是列举比n小的数 所以 每一位枚举结束后 就需要将这个位定下来 

花了不少时间入门。。继续干!

#include<cstdio>
#include<iostream>
#include<string.h>
using namespace std;
int dp[11][11]; 
int solve(int n)
{
    int dig[10],len=0;
    memset(dig,0,sizeof(dig));
    while(n>0)
    {
        dig[++len]=n%10;
        n=n/10;
    }
    dig[len+1]=0;
    int ans=0;
    for(int i=len;i;i--)//这里的思想 从最高位开始枚举 枚举完一位以后 由于是列举比n小的数 所以 每一位枚举结束后 就需要将这个位定下来 
    {
        for(int j=0;j<dig[i];j++)
        {
            if(j!=4&&!(dig[i+1]==6&&j==2))//判断此时的枚举是否合法 
            ans+=dp[i][j];    
        }
        if(dig[i]==4||(dig[i+1]==6&&dig[i]==2)) break;//之前位置定好了 如果出现了不满足条件的情况 后面继续下去就没有什么意思了 
    }
    return ans;
}
int main()
{
    int n,m;
    while(cin>>n>>m)
    {
        if(m+n==0) break;
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        for(int i=1;i<=7;i++)
        {
            for(int j=0;j<=9;j++)
            {
                for(int k=0;k<=9;k++)
                {
                    if(j!=4&&!(j==6&&k==2)) dp[i][j]+=dp[i-1][k];
                }
            }
        }
        cout<<solve(m+1)-solve(n)<<endl;// 这里要不要加1是要考虑区间的开闭情况 
    }
    return 0;
}
原文地址:https://www.cnblogs.com/z1141000271/p/5740020.html