zoukankan      html  css  js  c++  java
  • 递推、数位DP解析(以HDU 2089 和 HDU 3555 为例)

    HDU 2089 不要62

    题目链接

    http://acm.hdu.edu.cn/showproblem.php?pid=2089

    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
     
      1 /*
      2 问题
      3 输入一对整数n和m(0<n≤m<1000000), 如果遇到的都是0的整数对表示输入结束
      4 计算并输出有多少不含4和62(必须连号)的数字的个数
      5 
      6 解题思路
      7 首先的想法是将1000000以内的符合条件的数打表,然后在区间里数数就行了。
      8 参考网上的解答,这是一道数位DP,那么问题来了什么是数位DP呢?
      9     数位DP就是在根据每个数的各位数字的一些规律,能够得到一些递推关系,从而通过记忆化搜索来解决一些问题的思想。一般是区间内
     10 满足条件的数有多少个,相对于暴力破解,数位DP的解题方法更巧妙,更简洁、高效。
     11 
     12 拿不要62这道题来说,不吉利数字要么包含4,要么包含62(必须连号),反过来一个吉利数字需要不能有4,也不能有62
     13 所以定义变量为i和j,表示数字的长度是i,最高位是j
     14 那么状态就是f[i][j],表示数字的长度是i,最高位是j,符合条件的数有多少个
     15 可以根据规律写出如下递推式: 
     16 f[i][j]=
     17 
     18     if (j==4) f[i][j]=0
     19 
     20     else if (j!=6) f[i][j]=Σf[i-1][k] (k=0,1,2,3,4,5,6,7,8,9)
     21 
     22     else if (j==6) f[i][j]=Σf[i-1][k] (k=0,1,3,4,5,6,7,8,9) 
     23 
     24 有了这个二维表,我们只需要计算一个数x,0到x有多少个符合条件的数,最后左区间减去右区间几位答案。
     25 参考博客:https://blog.csdn.net/zhangxian___/article/details/75304335 
     26 */ 
     27 #include<cstdio>
     28 #include<cstring>
     29 const int maxn=10;
     30 int f[maxn][maxn],c[maxn];
     31 void getf();
     32 long long solve(int n);
     33 
     34 int main()
     35 {
     36     int n,m;
     37     getf();
     38     
     39     while(scanf("%d%d",&n,&m) == 2 && m+n != 0){
     40         long long a=solve(m+1);
     41         long long b=solve(n);
     42         
     43         printf("%lld
    ",a-b);
     44     }
     45     return 0;
     46 }
     47 
     48 long long solve(int n)
     49 {
     50     int a[10]={0},i,j,k;
     51     
     52     i=1; 
     53     while(n){
     54         a[i++] = n%10;
     55         n/=10;
     56     }
     57     /*for(j=1;j<i;j++)
     58         printf("@%d ",a[j]);
     59     printf("
    ");*/
     60     i--;//长度减一为最高位数字的下标 
     61     
     62     long long ans=0;
     63     for(k=i;k>=1;k--){//枚举每一位数字的下标 
     64         for(j=0;j<a[k];j++){//枚举0到每一位数字减一 
     65             if(j != 4 && !(a[k+1] == 6 && j == 2)){
     66                 ans += f[k][j];
     67             }    
     68         }
     69         if(a[k] == 4) break;
     70         if(a[k+1] == 6 && a[k] == 2) break;
     71     }
     72     return ans;
     73 }
     74 
     75 void getf()
     76 {
     77     int i,j,k;
     78     memset(f,0,sizeof(int)*maxn*maxn);
     79     f[0][0]=1;//0位数最高位是0,初始化为1 
     80     for(i=1;i<10;i++){
     81         for(j=0;j<10;j++){
     82             if(j == 4) f[i][j]=0;
     83             else if(j == 6)
     84             {
     85                 for(k=0;k<10;k++)
     86                     f[i][j] += f[i-1][k];
     87                 f[i][j] -= f[i-1][2];
     88             }
     89             else
     90             {
     91                 for(k=0;k<10;k++)
     92                     f[i][j] += f[i-1][k];
     93             }
     94         }
     95     }
     96     /*for(i=1;i<10;i++){
     97         for(j=0;j<10;j++){
     98             printf("%d ",f[i][j]);
     99         }
    100         printf("
    ");
    101     }*/
    102 }

     HDU 3555 Bomb

    题意

    给出一个N(1 <= N <= 2^63-1),问1到N中不含49序列的数字有多少个

    解题思路

      类似这种含某个序列的数的个数,我们可以将其转换成不含某个序列的数,然后用总的个数减去即可。

    我们用上面不含62的模板,求出1到N中不含49序列的数的个数,然后用总数减去。不同的是数组可能溢出,需要用long long 存储。

    代码:

    #include <cstdio>
    #include <cstring>
    
    typedef long long ll;
    const int maxn = 50;
    ll f[maxn][maxn];
    
    void getf() {
        memset(f, 0, sizeof(ll)*maxn*maxn);
        f[0][0] = 1;
        for(int i = 1; i < maxn; i++) {
            for(int j = 0; j < 10; j++) {
                for(int k = 0; k < 10; k++) {
                    f[i][j] += f[i - 1][k];
                }
                if(j == 4)
                    f[i][j] -= f[i - 1][9];
            }
        }    
    }
    
    ll solve(ll x) {
        int a[maxn] = {0};
        int i = 1;
        while(x) {
            a[i++] = x%10;
            x /= 10;
        }
        ll ans = 0;
        for(int k = i - 1; k >= 1; k--) {
            for(int j = 0; j < a[k]; j++) {
                if(!(j == 9 && a[k + 1] == 4))
                    ans += f[k][j];
            }
            if(a[k + 1] == 4 && a[k] == 9)    break;
        }
        return ans;
    }
    
    int main()
    {
        ll n;
        int T;
        scanf("%d", &T);
        while(T--) {
            getf();
            scanf("%I64d", &n);
            printf("%I64d
    ", n - solve(n + 1) + 1); 
        }
        return 0;
    }
  • 相关阅读:
    箭头函数的this、arguments详解
    webpack笔记
    关于proxy反向代理如何解决跨域问题的前世今生
    前端基础修炼日志(一):js内存管理机制
    前后端分离下的跨域问题以及CSRF攻击
    ES6之Promise
    浏览器缓存机制深入理解与实践(二):预加载
    浏览器缓存机制深入理解与实践(一)
    Go开发中的十大常见陷阱[译]
    网站必备之简繁切换功能实现
  • 原文地址:https://www.cnblogs.com/wenzhixin/p/8950873.html
Copyright © 2011-2022 走看看