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连号,所以不属于不吉利数字之列。
你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。
杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。
不吉利的数字为所有含有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 }
题意
给出一个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; }