数位dp解决的问题是指求在一段数的区间里面 满足条件的数的个数
核心为两点
http://wenku.baidu.com/link?url=tpfIYzhx_MzevpIM58UZ66pr-87MCFPKTMKFdGDi5jUqyO9ckti0mY6diSz2PZEL_ZBhd2zIbhus1mnzDiAO1B5K2Vu38YDsqjmOvYKFT6q
我自己总结下吧
第一 是dp的状态转移方程
dp[i][j] 表示的是以j开头的i位数 满足条件的个数为多少
既然是要记录满足条件的个 那么 dp[i][j]=sum(dp[i-1][0~9]) 这个不难理解
for(int i=1;i<=7;i++) { for(int j=0;j<=9;j++) { for(int k=0;k<=9;k++) { if(j!=4&&!(j==6&&k==2)) dp[i][j]+=dp[i-1][k]; } } }
然后就是solve函数问题 solve(i)这个函数的作用是求出【0,i)区间满足条件的数的个数
大致的思想 是从高位开始 逐一枚举比该为数小的数 在满足的条件的情况下 统计和 每次枚举完
举完一位以后 由于是列举比n小的数 所以 每一位枚举结束后 就需要将这个位定下来
花了不少时间入门。。继续干!
#include<cstdio> #include<iostream> #include<string.h> using namespace std; int dp[11][11]; int solve(int n) { int dig[10],len=0; memset(dig,0,sizeof(dig)); while(n>0) { dig[++len]=n%10; n=n/10; } dig[len+1]=0; int ans=0; for(int i=len;i;i--)//这里的思想 从最高位开始枚举 枚举完一位以后 由于是列举比n小的数 所以 每一位枚举结束后 就需要将这个位定下来 { for(int j=0;j<dig[i];j++) { if(j!=4&&!(dig[i+1]==6&&j==2))//判断此时的枚举是否合法 ans+=dp[i][j]; } if(dig[i]==4||(dig[i+1]==6&&dig[i]==2)) break;//之前位置定好了 如果出现了不满足条件的情况 后面继续下去就没有什么意思了 } return ans; } int main() { int n,m; while(cin>>n>>m) { if(m+n==0) break; memset(dp,0,sizeof(dp)); dp[0][0]=1; for(int i=1;i<=7;i++) { for(int j=0;j<=9;j++) { for(int k=0;k<=9;k++) { if(j!=4&&!(j==6&&k==2)) dp[i][j]+=dp[i-1][k]; } } } cout<<solve(m+1)-solve(n)<<endl;// 这里要不要加1是要考虑区间的开闭情况 } return 0; }