• UESTC 884 方老师的专题讲座 --数位DP


    定义:cnt[L][K]表示长度为L,最高位为K的满足条件C的个数。

    首先预处理出cnt数组,枚举当前长度最高位和小一个长度的最高位,如果相差大于2则前一个加上后一个的方法数。

    然后给定n,计算[1,n-1]中满足条件C的数的个数。

    设有K位数,则不足K位的累加,然后枚举K位数的情况,从高位到低位枚举,每次枚举到比该位小1的数,注意:如果某时刻该数中有两位相差大于2,则再枚举下去已经没有意义,因为以后的数再也不会满足条件C,这时退出即可。

    代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <string>
    #define Mod 1000000007
    #define ll long long
    using namespace std;
    #define N 100007
    
    ll cnt[22][12];  //cnt[L][K]:长度为L,最高位为K的满足条件C的数的个数
    ll NUM[21];
    
    ll DP(ll a)    //[1,n-1]
    {
        int i,j,K;
        int pre,head;
        ll res = 0;
        K = 0;
        while(K <= 18 && NUM[K] <= a)
            K++;
        K--;
        //cout<<"K is "<<K<<endl;
        for(i=1;i<K;i++)     //所有小于a长度的长度
            for(j=1;j<=9;j++)   //所有首位
                res += cnt[i][j];
        head = a/NUM[K];      //数a的首位
        for(i=1;i<head;i++)    //从1开始(不含前导0)
            res += cnt[K][i];
        a %= NUM[K];   //去除首位
        pre = head;    //多一位的首位
        for(i=K-1;i>=1;i--)  //长度逐次递减,高位到低位
        {
            head = a/NUM[i];
            for(j=0;j<head;j++)   //小于a的当前位的数做首位
                if(abs(pre-j) >= 2)
                    res += cnt[i][j];
            if(abs(head-pre) < 2)   //如果前面某两位出现差小于2,再枚举后面的数就没意义了,因为无论如何都不会满足了。
                break;
            pre = head;
            a %= NUM[i];  //一个一个去除
        }
        return res;
    }
    
    int main()
    {
        int i,j,l,k;
        ll a,b;
        j = 0;
        NUM[j++] = 0LL;
        for(i=1;i<=19;i++)
            NUM[j++] = (ll)pow(10,i-1);
        //for(i=0;i<j;i++)
            //cout<<NUM[i]<<" ";
        //cout<<endl;
        memset(cnt,0,sizeof(cnt));
        for(k=0;k<=9;k++)
            cnt[1][k] = 1;
        for(l=2;l<=18;l++)
        {
            for(i=0;i<=9;i++)   //长度为l时的首位
            {
                for(k=0;k<=9;k++)   //长度为l-1时的首位
                {
                    if(abs(k-i) >= 2)
                        cnt[l][i] += cnt[l-1][k];
                }
            }
        }
        for(k=0;k<=9;k++)  //10^18
            if(abs(1-k) >= 2)
                cnt[19][1] += cnt[18][k];
    
        while(scanf("%lld%lld",&a,&b)!=EOF)
        {
            printf("%lld
    ",DP(b+1)-DP(a));
        }
        return 0;
    }
    View Code
  • 相关阅读:
    OAuth2.0的四种授权模式
    Jedis整合单机、Sentinel和Cluster模式
    Redis Cluste部署
    Web-动态页面
    Web开发-Servlet&HTTP&Request
    Ajax&Json
    Web开发之Tomcat&Servlet
    PagedListCore的使用
    自己写一个依赖注入容器Container
    在core2.0中实现按程序集注入依赖
  • 原文地址:https://www.cnblogs.com/whatbeg/p/3762744.html
Copyright © 2020-2023  润新知