递推、数位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\n",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("\n");*/
 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("\n");
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", n - solve(n + 1) + 1); 
    }
    return 0;
}

 

posted @ 2018-04-26 11:46  Reqaw  阅读(436)  评论(0编辑  收藏  举报