• 数位dp知识点整理


    题解报告:hdu 2089 不要62

    Problem Description

    杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer)。
    杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。
    不吉利的数字为所有含有4或62的号码。例如:
    62315 73418 88914
    都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。
    你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。

    Input

    输入的都是整数对n、m(0<n≤m<1000000),如果遇到都是0的整数对,则输入结束。

    Output

    对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。

    Sample Input

    1 100
    0 0

    Sample Output

    80
    解题思路:模拟计数走一遍流程就清楚了,详解看代码。
    AC代码一:
     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int n,m,dp[10][10],d[10];
     4 void init(){
     5     memset(dp,0,sizeof(dp));
     6     dp[0][0]=1;///特殊定义0位数首位是0的方案数为1
     7     for(int i=1;i<10;++i){///表示i位数,最高位从1开始
     8         for(int j=0;j<10;++j){///表示i位数的首位数字是j--->(0~9)
     9             if(j==4)continue;///如果当前首位为4,dp[i][j]=0
    10             ///累加i-1位数的首位是k的方案数,因为最高位已经确定,所以只需累加比其小的方案数即可
    11             for(int k=0;k<10;++k){
    12                 if(j==6&&k==2)continue;///当前首位j与其右边这一位上的数字k不能组成62
    13                 dp[i][j]+=dp[i-1][k];///状态转移方程
    14             }
    15         }
    16     }
    17 }
    18 int solve(int x){///累加小于x的所有数的方案数,直到遇到4或者是62就退出累加
    19     memset(d,0,sizeof(d));///d数组记录x每一位上的数字
    20     int len=0,ans=0;
    21     while(x){///得到x的每一位数字
    22         d[++len]=x%10;
    23         x/=10;
    24     }
    25     ///d[len+1]=0;///前面已经置0,避免产生上一次的结果6对这个统计的影响
    26     for(int i=len;i>=1;--i){///从高位向低位(从大到小)枚举位数i
    27         for(int j=0;j<d[i];++j){///巧妙处理:每当进入到下一位,就默认上一位确定
    28             if(d[i+1]==6&&j==2)continue;///跳过62,累加所有方案数
    29                 ans+=dp[i][j];///dp[i][4]都为0,所以这里无需判断j==4
    30         }
    31         ///如果该位上是4或者(上一位是6并且当前位上是2)则直接退出后面的方案数的累加,因为后面的都不合法了
    32         if(d[i]==4||(d[i+1]==6&&d[i]==2))break;
    33     }
    34     return ans;
    35 }
    36 int main(){
    37     init();///预处理当前第i位首位是j符合条件的方案数
    38     ///for(int i=0;i<10;++i)cout<<dp[i][4]<<endl;
    39     while(~scanf("%d%d",&n,&m)&&(n|m)){
    40         printf("%d
    ",solve(m+1)-solve(n));
    41         ///差分思想,需要将[0,m+1)即[0,m]中的所有方案数减去[0,n)中的方案数才能得到[n,m]中满足条件的所有情况数
    42     }
    43     return 0;
    44 }

    AC代码二:记忆化搜索。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int n,m,dp[10][2],d[10];
     4 ///dp[pos][status]来保存在第pos位,status表示上一位是否为6这个状态
     5 int dfs(int pos,bool if6,bool limit){///limit用来判断前一位是否为数位上界,是则本位不能取到大于a[pos]的数
     6     if(!pos)return 1;///特殊情况下为1,即dp[0][0]=1;
     7     if(!limit&&dp[pos][if6]!=-1)return dp[pos][if6];///若前一位不是上限,并且dp[pos][if6]已确定,直接return统计,这里体现了记忆化搜索
     8     int up=limit?d[pos]:9,ans=0;///limit判断pos前的几位数字是否与n一样
     9     for(int i=0;i<=up;++i){
    10         if(i==4||(if6&&i==2))continue;///这里is_6的值是0/1,用来区分前一位是否为6
    11         ///cout<<"当前第"<<pos<<"位,上限为"<<limit<<",前一个数是否为6:"<<if6<<",当前位为"<<i<<endl;
    12         ans+=dfs(pos-1,i==6,limit&&i==up);
    13     }
    14     return limit?ans:(dp[pos][if6]=ans);///若前一位不是上限,即这一位可以达到最大,则更新dp值
    15 }
    16 int solve(int x){
    17     memset(d,0,sizeof(d));
    18     int len=0;
    19     while(x){d[++len]=x%10;x/=10;}
    20     return dfs(len,false,true);///刚开始前0位是相同的
    21 }
    22 int main(){
    23     memset(dp,-1,sizeof(dp));
    24     while(~scanf("%d%d",&n,&m)&&(m|n)){
    25         printf("%d
    ",solve(m)-solve(n-1));
    26     }
    27     return 0;
    28 }
  • 相关阅读:
    Theme.AppCompat.Light报错
    在Eclipse添加Android兼容包( v4、v7 appcompat )
    如何从Eclipse导入github上的项目源码
    一个C#多线程的工作队列
    是否需要手动执行DataContext的Dispose方法?
    Unity3d 销毁
    Unity3d 碰撞检测
    unity3d 鼠标事件
    Unity3d 刚体
    unity3d 让物体移动到点击位置
  • 原文地址:https://www.cnblogs.com/acgoto/p/10047009.html
Copyright © 2020-2023  润新知