zoukankan      html  css  js  c++  java
  • 数位DP

    什么是数位 DP

    在信息学竞赛中,有一类难度不大但异常麻烦的问题——数位计数问题,这类问题的主要特点是询问的答案和一段连续的数的各个数位相关,并且需要对时间效率有一定要求。由于解决这类问题往往意味着巨大的代码量,而众多的特殊情况又意味着出现错误的巨大可能性,因此很少有人愿意解决此类问题,但只要掌握好的方法,解决这类问题也并非想象中的那样困难。                

                                                             ---------高逸涵《数位计数问题解法研究》

    数位DP的解题思路

    对于一个数,若其首位已经比询问上界小,则剩余位没有任何限制。此时如果能直接处理这一情况,则问题距离解决又会迈出一大步。
    例如,在十进制下,计算[10000,54321]内的数字和,我们可以将其分解为:
    [10000,19999],[20000,29999],[30000,39999],[40000,49999],[50000,54321]。
    前四个区间如果可以直接解决,则只需处理最后一个区间,进一步将最后一个区间划分为:
    [50000,50999],[51000,51999],[52000,52999],[53000,53999],[54000,54321]。
    同理将最后一个区间划分下去,最后可以得到以下区间划分:
    [10000,19999],[20000,29999],[30000,39999],[40000,49999],
    [50000,50999],[51000,51999],[52000,52999],[53000,53999],
    [54000,54099],[54100,54199],[54200,54299],
    [54300,54309],[54310,54319],
    [54320,54321]

    数位DP的经典模板

    该模板出处不详,大大降低了数位DP类问题的难度。

     1 typedef long long LL;
     2 const int maxn=22;
     3 int dig[maxn];
     4 LL f[maxn]/* [TODO] */;
     5 
     6 LL dfs(int pos,/* TODO */,int limit){
     7     if (pos<0) return /* TODO */;
     8     if (!limit&&f[pos]/* [TODO] */!=-1) return f[pos]/* [TODO] */;
     9     LL res=0;
    10     int last=limit?dig[pos]:9;
    11     for (int i=0;i<=last;i++){
    12         res+=dfs(pos-1,/* TODO */,limit&&(i==last));
    13     }
    14     if (!limit) f[pos]/* [TODO] */=res;
    15     return res;
    16 }
    17 
    18 LL solve(LL n){
    19     int len=0;
    20     while (n){
    21         dig[len++]=n%10;
    22         n/=10;
    23     }
    24     return dfs(len-1,/* TODO */,1);
    25 }
    数位DP模板

    经典题目

    HDU 2089 不要62

    求给定区间中不含有62和4的数的个数。

     1 LL dfs(int pos,int pre,int fg,int limit){
     2     if (pos<0) return fg==0;
     3     if (!limit&&f[pos][pre][fg]!=-1) return f[pos][pre][fg];
     4     LL res=0;
     5     int last=limit?dig[pos]:9;
     6     for (int i=0;i<=last;i++){
     7         res+=dfs(pos-1,i,fg||((pre==6)&&(i==2))||(i==4),limit&&(i==last));
     8     }
     9     if (!limit) f[pos][pre][fg]=res;
    10     return res;
    11 }
    HDU 2089 不要62

    HDU 3555 Bomb

    求给定区间的含有49的数的个数。

     1 LL dfs(int pos,int pre,int istrue,int limit){
     2     if (pos<0) return istrue;
     3     if (!limit && f[pos][pre][istrue]!=-1) return f[pos][pre][istrue];
     4     int last=limit?dig[pos]:9;
     5     LL ret=0;
     6     for (int i=0;i<=last;i++){
     7         int ok=(pre==4)&&(i==9);
     8         ret+=dfs(pos-1,i,istrue||ok,limit&&(i==last));
     9     }
    10     if (!limit) f[pos][pre][istrue]=ret;
    11     return ret;
    12 }
    HDU 3555 Bomb

    windy 数

    求给定区间范围内的,求相邻数位之差绝对值不小于2的数的个数。

     1 LL dfs(int pos,int pre,int fg,int limit){
     2     if (pos<0) return 1;
     3     if (!limit && f[pos][pre][fg]!=-1) return f[pos][pre][fg];
     4     int last=limit?dig[pos]:9;
     5     LL ret=0;
     6     for (int i=0;i<=last;i++){
     7         if (fg==0||abs(i-pre)>=2)
     8             ret+=dfs(pos-1,i,fg||i,limit&&(i==last));
     9     }
    10     if (!limit) f[pos][pre][fg]=ret;
    11     return ret;
    12 }
    windy 数

    HDU 3709 Balanced Number

    平衡数。数n以数n中的某个位为支点,每个位上的数权值为(数字xi*(posi - 支点的posi)),如果数n里有一个支点使得所有数权值之和为0那么她就是平衡数。比如4139,以3为支点,左边 = 4 * (4 - 2) + 1 * (3 - 2) = 9,右边 = 9 * (1 - 2) = -9,左边加右边为0,所以4139是平衡数。现在给出一个区间[l,r],问区间内平衡数有多少个?

     1 LL dfs(int pos,int o,int pre,int limit){
     2     LL res=0;
     3     if (pos<0)    return pre==0;
     4     if (!limit&&f[pos][o][pre]!=-1) return f[pos][o][pre];
     5     int last=limit?dig[pos]:9;
     6     for (int i=0;i<=last;i++){
     7         res+=dfs(pos-1,o,pre+i*(pos-o),limit&&(i==last));
     8     }
     9     if (!limit) f[pos][o][pre]=res;
    10     return res;
    11 }
    12 
    13 LL solve(LL n){
    14     if (n<0) return 0;
    15     if (n==0) return 1;
    16     int len=0;
    17     while (n){
    18         dig[len++]=n%10;
    19         n/=10;
    20     }
    21     LL ans=0;
    22     for (int i=0;i<len;i++){
    23         ans+=dfs(len-1,i,0,1);
    24     }
    25     ans=ans-len+1;
    26     return ans;
    27 }
    HDU 3709 Balanced Number

    CodeForces 55D Beautiful numbers

    如果一个数能够被其每个数位的数都整除,那么这个数就叫做美丽数。

     1 int check(int bit,int mod){
     2     for (int i=2;i<=9;i++){
     3         if (bit&(1<<(i-2))){
     4             if (mod%i) return 0;
     5         }
     6     }
     7     return 1;
     8 }
     9 
    10 LL dfs(int pos,int bit,int mod,int limit){
    11     if (pos<0) return check(bit,mod);
    12     if (!limit && f[pos][bit][mod]!=-1) return f[pos][bit][mod];
    13     int last=limit?dig[pos]:9;
    14     LL ret=0;
    15     for (int i=0;i<=last;i++){
    16         int nbit=bit;
    17         if (i>=2) nbit|=1<<(i-2);
    18         ret+=dfs(pos-1,nbit,(mod*10+i)%MOD,limit&&(i==last));
    19     }
    20     if (!limit) f[pos][bit][mod]=ret;
    21     return ret;
    22 }
    CF 55D Beautiful numbers

    HDU 3652 B-number

    求小于n是13的倍数且含有'13'的数的个数。

     1 LL dfs(int pos,int pre,int fg,int md,int limit){
     2     if (pos<0) return fg&&(md==0);
     3     if (!limit&&f[pos][pre][fg][md]!=-1) return f[pos][pre][fg][md];
     4     LL res=0;
     5     int last=limit?dig[pos]:9;
     6     for (int i=0;i<=last;i++){
     7         res+=dfs(pos-1,i,fg||(pre==1&&i==3),(md*10+i)%13,limit&&(i==last));
     8     }
     9     if (!limit) f[pos][pre][fg][md]=res;
    10     return res;
    11 }
    HDU 3652 B-number

    HDU 4352 XHXJ's LIS

    求[L,R]内最长递增子序列是k的数的个数。

     1 LL f[maxn][1<<10][11];
     2 int K;
     3 int dig[maxn];
     4 int getLIS(int bit){
     5     int res=0;
     6     for (int i=0;i<10;i++){
     7         if (bit&(1<<i)) res++;
     8     }
     9     return res;
    10 }
    11 int gaoBit(int bit,int d){
    12     if (bit&(1<<d)) return bit;
    13     if ((1<<d)>bit) return bit|(1<<d);
    14     bit|=(1<<d);
    15     for (int i=d+1;i<10;i++){
    16         if (bit&(1<<i)) return bit^(1<<i);
    17     }
    18     return 0;
    19 }
    20 
    21 LL dfs(int pos,int bit,int limit){
    22     if (pos<0) return getLIS(bit)==K;
    23     if (!limit&&f[pos][bit][K]!=-1) return f[pos][bit][K];
    24     LL res=0;
    25     int last=limit?dig[pos]:9;
    26     for (int i=0;i<=last;i++){
    27 
    28         int go;
    29         if (bit==0&&i==0) go=0;
    30         else go=gaoBit(bit,i);
    31         res+=dfs(pos-1,go,limit&&(last==i));
    32     }
    33     if (!limit) f[pos][bit][K]=res;
    34     return res;
    35 }
    36 
    37 LL solve(LL n){
    38     int len=0;
    39     while (n){
    40         dig[len++]=n%10;
    41         n/=10;
    42     }
    43     return dfs(len-1,0,1);
    44 }
    HDU 4352 XHXJ's LIS

    HDU 4507 吉哥系列故事——恨7不成妻

    中文题。需要推公式。

     1 #include <iostream>
     2 #include <cstring>
     3 
     4 using namespace std;
     5 typedef long long LL;
     6 const int maxn=22;
     7 const int MOD=1e9+7;
     8 
     9 
    10 typedef pair<LL,LL> PII;
    11 typedef pair<PII,LL> PIII;
    12 
    13 inline LL fst(PIII a){
    14     return a.first.first;
    15 }
    16 inline LL sec(PIII a){
    17     return a.first.second;
    18 }
    19 inline LL thd(PIII a){
    20     return a.second;
    21 }
    22 inline PIII makeZeroPIII(){
    23     return make_pair(make_pair(0,0),0);
    24 }
    25 inline PIII makeOnePIII(){
    26     return make_pair(make_pair(1,0),0);
    27 }
    28 LL power[maxn];
    29 PIII f[maxn][11][8];
    30 bool v[maxn][11][8];
    31 int dig[maxn];
    32 
    33 PIII gao(PIII a, PIII b,int i,int pos){
    34     PIII res=makeZeroPIII();
    35     res.first.first=(fst(a)+fst(b))%MOD;//数量
    36     res.first.second=(sec(a)+sec(b)+((i*power[pos])%MOD*fst(b))%MOD)%MOD;//
    37     res.second=(thd(a)+thd(b)+((2*i*power[pos])%MOD*sec(b))%MOD+(((i*i*power[pos])%MOD*power[pos])%MOD*fst(b))%MOD)%MOD;//平方和
    38     return res;
    39 }
    40 
    41 PIII dfs(int pos,int pre,int sev,int limit){
    42     if (pos<0) {
    43         if (pre!=0&&sev!=0) return makeOnePIII();
    44         else return makeZeroPIII();
    45     }
    46     if (!limit&&v[pos][pre][sev]) return f[pos][pre][sev];
    47     PIII res=makeZeroPIII();
    48     int last=limit?dig[pos]:9;
    49     for (int i=0;i<=last;i++){
    50         if (i==7) continue;
    51         PIII tmp=dfs(pos-1,(pre*10+i)%7,(sev+i)%7,limit&&(i==last));
    52         res=gao(res,tmp,i,pos);
    53     }
    54     if (!limit){
    55         v[pos][pre][sev]=true;
    56         f[pos][pre][sev]=res;
    57     }
    58     return res;
    59 }
    60 
    61 LL solve(LL n){
    62     int len=0;
    63     while (n){
    64         dig[len++]=n%10;
    65         n/=10;
    66     }
    67     PIII ans = dfs(len-1,0,0,1);
    68     return thd(ans);
    69 }
    70 
    71 int main()
    72 {
    73     memset(v,0,sizeof(v));
    74     memset(power,0,sizeof(power));
    75     power[0]=1;
    76     for (int i=1;i<maxn;i++){
    77         power[i]=(power[i-1]*10)%MOD;
    78     }
    79     int T;
    80     cin>>T;
    81     while (T--){
    82         LL a,b;
    83         cin>>a>>b;
    84         cout<<(solve(b)-solve(a-1)+MOD)%MOD<<endl;
    85     }
    86     return 0;
    87 }
    HDU 4507 吉哥系列故事——恨7不成妻
  • 相关阅读:
    JS随笔
    tp5 redis 单例模式 转载
    分享我编程工作经历及对软件开发前景的看法
    redis详解(一)-- 概述
    redis详解(二)-- 数据类型详解
    redis详解(四)-- 高可用分布式集群
    redis详解(三)
    新工科平台
    关于Nginx的负载均衡
    微信退款回调
  • 原文地址:https://www.cnblogs.com/zinthos/p/3899606.html
Copyright © 2011-2022 走看看