hdu2089数位DP

旁听途说这个名字很久了,了解了一下。

改题目的意思是给你若干区间,让你找寻区间内不含62或4的数。

首先暴力必然T。。。那么实际上就是说,想办法做一种预处理,在每次输入的时候取值运算就可以了。

既然是DP先说一下dp[ i ][ j ]:

表示有 i 位并且最高为是 j 的数包含多少符合条件的数。

(读者仔细思考一下为什么这样设定,为什么这样子可以得到答案,实际dp训练的就是这个)

然后接下来最重要的就是DP转移方程了,首先对于i等于1的情况来说直接赋值就好了。

如果 i 不等于1,则传递关系如下:

dp[ i ][ j ]=  if ( j == 4 ) dp[ i ][ j ]=0

     else if ( j != 6) dp[ i ][ j ]=Σdp[ i -1][ k ] (k=0,1,2,3,4,5,6,7,8,9)

     else if ( j == 6) dp[ i ][ j ]=Σdp[ i -1][ k ] (k=0,1,3,4,5,6,7,8,9)

通俗的言语表达就是,如果最高为j,j又是4,那直接gg,等于0,其余如果j不是6,那么递归的时候无需考虑右边一位是不是2,直接全加,

最后如果j是6,那么右一位除了是2的情况全加,这就是递归方程了。

在如上计算之后,我们可以通过对dp数组中不同的值的拼凑来得到0到某个数的区间内有多少个符合条件的数。

然后知道形如  [0, i )这样的区间内的个数,如果给定任意区间 [a , b],则可以拆分成两个区间相间即[0 , b) - [0 , a)

那么怎么拼凑呢?

比如345这个数,假设a1是最高位3,a2是4,a3是5,

那么我们就取最高位为0,1,2的所有数和,加上最高位为3,次高位为0,1,2,3(4不能选,虽然比5小,但是有4了)的所有之和,再加上。。。类比下去。

代码如下:

#include<iostream>
using namespace std;
#define ll long long
ll dp[10][10];

void cal_dp()
{
    dp[0][0]=1;
    for (int i=1;i<10;i++)
    {
        for (int j=0;j<10;j++)
        {
            if (j==4) dp[i][j]=0;
            else if (j==6)
            {
                for (int k=0;k<10;k++)
                    dp[i][j]+=dp[i-1][k];
                dp[i][j]-=dp[i-1][2];
            }
            else
            {
                for (int k=0;k<10;k++)
                    dp[i][j]+=dp[i-1][k];
            }
        }
    }
}

int a[10];
ll solve(int n)
{
    a[0]=0;
    while (n)
    {
        a[++a[0]]=n%10;
        n/=10;
    }
    a[a[0]+1]=0;
    ll ans=0;
    for (int i=a[0];i>=1;i--)
    {
        for (int j=0;j<a[i];j++)
            if (j!=4 && !(a[i+1]==6 && j==2))
                ans+=dp[i][j];
        if (a[i]==4) break;
        if (a[i+1]==6 && a[i]==2) break;
    }
    return ans;
}

int main()
{
    int n,m;
    cal_dp();
    while (scanf("%d %d",&n,&m)==2 && (n||m))
    {
        ll k1=solve(m+1);
        ll k2=solve(n);
        printf("%I64d\n",k1-k2);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/fantastic123/p/9054222.html
今日推荐