zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    .net注册iis
    hdu 1081To The Max
    hdu 1312Red and Black
    hdu 1016Prime Ring Problem
    hdu 1159Common Subsequence
    hdu 1372Knight Moves
    hdu 1686Oulipo
    hdu 1241Oil Deposits
    hdu 1171Big Event in HDU
    hdu 4006The kth great number
  • 原文地址:https://www.cnblogs.com/whatbeg/p/3762744.html
Copyright © 2011-2022 走看看