【解决问题】
一个极大的多位数字中,存在某种特殊情况出现多少次
通过记忆化和动态规划的方法,将之前的情况数记录下来,在计算其他情况的时候,通过状态的转移相互利用
【模板】
【题意】求1~n之间有多少个数又含有13又能被13整除,(1<=n<=1e9)
【代码】
#include<cstdio> #include<cstring> #define MAXN 20 int dp[MAXN][MAXN][10]; int maxx[MAXN]; //maxx记录整个数字的上限 //len 表示当前枚举的数字长度 //mod 表示状态之一,现在这个长度对16取余的结果 //stu 状态之一,当前的数字是否存在 //lim 状态之一,前面的数字是否达到上限,因为枚举的情况在前面没有达到的话,后面的是可以任取的 // 而如果达到的话,就需要考虑数字的取值范围了 int Geta(int len,int mod,int stu,bool lim) { //从后往前开始枚举数字的情况 if(len==0) //当所有位置上的数字列举完成之后 return mod==0&&stu==2; //返回存在的情况 if(!lim && dp[len][mod][stu]) return dp[len][mod][stu]; //max 表示需要遍历到的最大的数字 int cnt=0,max=lim?maxx[len]:9; for(int i=0;i<=max;i++) { int ns=stu; //状态的转换 if(stu!=2&&i==1)//如果数字没有13,并且此位为1,那么标记为状态1 ns=1; if(stu!=2&&i!=1)//如果此位不为1,那么就什么都没有,即为0 ns=0; if(stu==1&&i==3)//如果前一位是1,并且这一位是3的话,这个数字就拥有了13 ns=2; //注意顺序 cnt+=Geta(len-1,(mod*10+i)%13,ns,lim&&i==max); //将短的情况进行枚举,这里枚举的i就是当前位置的数字 // lim && i==max 是否达到上限,如果lim没有达到,后面取什么都无所谓了 // 如果前面达到,但是当前位置达到 } if(!lim)//没有超过的话,就将计数返还 dp[len][mod][stu]=cnt; return cnt; } int main() { int n; while(~scanf("%d",&n)) { memset(dp,0,sizeof(dp)); int len=0; while(n) { maxx[++len]=n%10; n/=10; } printf("%d\n",Geta(len,0,0,1)); } }
来源:https://www.cnblogs.com/rentu/p/12295558.html