旁听途说这个名字很久了,了解了一下。
改题目的意思是给你若干区间,让你找寻区间内不含62或4的数。
首先暴力必然T。。。那么实际上就是说,想办法做一种预处理,在每次输入的时候取值运算就可以了。
既然是DP先说一下dp[ i ][ j ]:
表示有 i 位并且最高为是 j 的数包含多少符合条件的数。
(读者仔细思考一下为什么这样设定,为什么这样子可以得到答案,实际dp训练的就是这个)
然后接下来最重要的就是DP转移方程了,首先对于i等于1的情况来说直接赋值就好了。
如果 i 不等于1,则传递关系如下:
dp[ i ][ j ]= if ( j == 4 ) dp[ i ][ j ]=0
else if ( j != 6) dp[ i ][ j ]=Σdp[ i -1][ k ] (k=0,1,2,3,4,5,6,7,8,9)
else if ( j == 6) dp[ i ][ j ]=Σdp[ i -1][ k ] (k=0,1,3,4,5,6,7,8,9)
通俗的言语表达就是,如果最高为j,j又是4,那直接gg,等于0,其余如果j不是6,那么递归的时候无需考虑右边一位是不是2,直接全加,
最后如果j是6,那么右一位除了是2的情况全加,这就是递归方程了。
在如上计算之后,我们可以通过对dp数组中不同的值的拼凑来得到0到某个数的区间内有多少个符合条件的数。
然后知道形如 [0, i )这样的区间内的个数,如果给定任意区间 [a , b],则可以拆分成两个区间相间即[0 , b) - [0 , a)
那么怎么拼凑呢?
比如345这个数,假设a1是最高位3,a2是4,a3是5,
那么我们就取最高位为0,1,2的所有数和,加上最高位为3,次高位为0,1,2,3(4不能选,虽然比5小,但是有4了)的所有之和,再加上。。。类比下去。
代码如下:
#include<iostream> using namespace std; #define ll long long ll dp[10][10]; void cal_dp() { dp[0][0]=1; for (int i=1;i<10;i++) { for (int j=0;j<10;j++) { if (j==4) dp[i][j]=0; else if (j==6) { for (int k=0;k<10;k++) dp[i][j]+=dp[i-1][k]; dp[i][j]-=dp[i-1][2]; } else { for (int k=0;k<10;k++) dp[i][j]+=dp[i-1][k]; } } } } int a[10]; ll solve(int n) { a[0]=0; while (n) { a[++a[0]]=n%10; n/=10; } a[a[0]+1]=0; ll ans=0; for (int i=a[0];i>=1;i--) { for (int j=0;j<a[i];j++) if (j!=4 && !(a[i+1]==6 && j==2)) ans+=dp[i][j]; if (a[i]==4) break; if (a[i+1]==6 && a[i]==2) break; } return ans; } int main() { int n,m; cal_dp(); while (scanf("%d %d",&n,&m)==2 && (n||m)) { ll k1=solve(m+1); ll k2=solve(n); printf("%I64d\n",k1-k2); } return 0; }