【算法•日更•第十三期】信息奥赛一本通1589:不要 62题解

  废话不多说,直接上题:


1589:不要 62


时间限制: 1000 ms         内存限制: 524288 KB
提交数: 105     通过数: 67 

【题目描述】

原题来自:HDU 2089

杭州人称那些傻乎乎粘嗒嗒的人为 62(音:laoer)。

杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。

不吉利的数字为所有含有 4 或 62 的号码。例如:62315,73418,8891462315,73418,88914 都属于不吉利号码。但是,61152 虽然含有 6 和 2,但不是 62 连号,所以不属于不吉利数字之列。

你的任务是,对于每次给出的一个牌照区间号,推断出交管局今后又要实际上给多少辆新的士车上牌照了。

【输入】

输入的都是整数对 n,m,如果遇到都是 0 的整数对,则输入结束。

【输出】

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

【输入样例】

1 100
0 0

【输出样例】

80

【提示】

数据范围与提示:

对于全部数据,0<nm<107 。

【来源】


  这道题乍一看挺简单,但是看一眼心态就崩了。107,而且还是多次输出,一看就是要预处理,像这种要预处理的而且和数位有关的,使我们会想到数位动态规划。

  小编最开始的想法很简单,f[i]表示0~i之间的合法个数,那么就有f[i]=f[i-1]+(i==合法);(伪代码),那么答案就是f[m]-f[n-1],似乎是区间动态规划的方法。但是时间太慢了,小编也觉得不太可能。

  这是小编想到的状态,实在想不到和数位有关的方法了。看了看大佬的博客才发现状态原来可以这样设计:用f[i][j]表示以j开头的i位数的合法个数。

  那么就很清晰了,就有f[i][j]=sum{f[i-1][k]},其中k!=4,j!=4,!(k==2&&j==6),举个栗子:比如说f[3][1]=f[2][0]+f[2][1]+f[2][2]+f[2][3]+f[2][5]+f[2][6]+f[2][7]+f[2][8]+f[2][9]。

   那么剩下的问题就是怎么处理区间[n,m],把它分为[0,n]和[0,m+1](知道你会说什么,这和后面的算法有关)

  先不说n和m,假设现在要统计0~71806291的区间,那么我们就可以遍历各个数位i,同时枚举各数位数字j,如果合法,那么就把答案加上f[i][j]就可以了,如果发生了这样的情况:题目中的数字不合法,那么就加上f[i][j]后退出循环,因为后面的都计过了。比如说6291,先到了6,加上5000个数的合法个数,到了2会加上2000个数的合法个数,然后退出循环,就会发现前6200的合法个数都加上了,而后面的就不加了,因为这是建立在6200的基础上的,一定不合法。

  好了,详见注释,代码如下:

 1 #include<iostream>
 2 using namespace std;
 3 int n,m,f[1000][1000];
 4 int calc(int x)//计算0~x区间内合法个数 
 5 {
 6     int s[1000]={0},cnt=0,ans=0;
 7     while(x)//分解数位 
 8     {
 9         s[++cnt]=x%10;
10         x/=10;
11     }
12     for(int i=cnt;i>=1;i--)
13     {
14         for(int j=0;j<=s[i]-1;j++) //因为后面的数不能超过,所以只能到s[i]-1 
15         if(!(j==4||(j==2&&s[i+1]==6))) ans+=f[i][j];//合法就加上 
16         if(s[i]==4||(s[i]==2&&s[i+1]==6)) break;//数字中不合法就退出循环 
17     }
18     return ans;
19 }
20 int main()
21 {
22     f[0][0]=1;
23     for(int i=1;i<=7;i++)//预处理 
24     for(int j=0;j<=9;j++)
25     for(int k=0;k<=9;k++)
26     if(j!=4&&!(j==6&&k==2)) f[i][j]+=f[i-1][k];
27     while(1)
28     {
29         cin>>n>>m;
30         if(n==0&&m==0) break;
31         cout<<calc(m+1)-calc(n)<<endl;
32     }
33     return 0;
34 }
原文地址:https://www.cnblogs.com/TFLS-gzr/p/11194885.html