数位DP--由一道微软笔试题引起

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               

前天晚上,一位研三的学长突然跑到我们宿舍,问我们一道微软笔试题。给你一个整数n,求出1到n这个区间范围内包含数字0的个数,例如当n=10的时候就只有10包含0,输出1,n=90就输出9。唯一的要求是此题不能用遍历来实现,时间负责度要比O(n)小,要是遍历显然谁都会做。

初看此题,似乎能找到规律,应该是排列组合的思想,下面是我认识的一个学数学的同学提供的思路:




用数学方法看起来应该能够解决,不过并没有尝试。在问了一个ACM大牛后,得到了一个名词“数位DP”,并且发现其实有很多与此类似的题目,都可以用数位DP的方法求解。

下面先给出数位DP的背景:

•在给定区间[A,B]内,找满足要求的数。
•要求一般和数大小无关,而与数的组成有关
•例如,递增的,1234, 2579…
•         双峰的,19280,26193…
•         含49的,49, 149, 1492… 
•         整除13的,26, 39…
•麻烦在于,规模大,位数> 100,不能枚举。
•区间往往不是整百整千,边界问题
•注意
–记忆化搜索思路清晰
–开适当空间
–寻找合适的状态,简化计算量

为了降低时间复杂度,可以借鉴传统DP中状态转换,打表这些思路,得到了数位DP:

F(A,B) = F(B,0)-F(A-1,0)

暴力+存储 = 记忆化搜索

•暴力:
•暴力枚举每一位(0..9),注意区间边界;与符号的匹配。

•dfs(i,j,k,flag)
•枚举第i位的数,匹配str[j],前一位是k,是否达到上限(flag=true,false)
•达到了上限则只能枚举0..num[i],否则可以枚举0..9
•存储
•dfs(i,j,k,flag)
•设状态与递归参数一致f[i][j][k][flag],表示当枚举到第i位的数,匹配str[j],前一位是k,是否达到上限(flag=true,false)时,满足要求的数字个数。
•dfs的过程,相当于在填充f,假设f的空间O(100*10*10*2),则dfs的时间O(20000)

针对上面几种类型的问题,数位DP解决方案如下:(具体可以看http://www.cppblog.com/Yuan/archive/2011/07/15/139299.html

•整除13
•dfs(i, m, flag)
•枚举第i位数,前面枚举出的数模13的余数m,是否到达上限flag
•整除自身各位数CF55D
•dfs(i, m, l, flag)
•枚举第i位数,前面枚举出的数模LCM(0..9),LCM(前面枚举出的数),是否达到上限
•包含”49”
•dfs(i, k, find, flag)
•枚举第i位数,前一位是k,是否已包含”49”(find),是否达到上限
•分类讨论:前一位是否为4,当前是否已包含“49”

在这几种类型中,包含49的与微软这道题最为相近,不过要注意的是运算过程中需要把前缀0的情况剔除,最终代码如下:

#include<iostream>#include<algorithm>#include<cstdlib>#include<cstring>using namespace std;typedef long long ll;#define mem(a,b) memset(a,b,sizeof(a))const int L = 20, P = 1e9+7;struct RES { ll all, sum, cnt; RES() {} RES(int i,int j,int k):all(i),sum(j),cnt(k) {}} dp[L];ll chkmod(ll x,ll p) return (x%p+p)%p;}int d[L], n;RES dfs(int pos, int UP) if(pos<0) {  return RES(0,0,1); } if(!UP && ~dp[pos].all) {  return dp[pos]; } RES ret(0,0,0)int up=UP?d[pos]:9; ret.all += dfs(pos-1, UP&&up==0).all; ret.all %= P; for(int i=1;i<=up;i++)  {  int nUP = UP&&i==up;  for(int j=pos-1;j>=-1;j--)   {   ll tmp = dfs(j, nUP).sum + dfs(j, nUP).cnt * (pos - 1 - j);   tmp %= P;   ret.all += tmp;   ret.all %= P;   ret.sum += tmp;   ret.sum %= P;   ret.cnt += dfs(j, nUP).cnt;   ret.cnt %= P;   nUP = nUP && d[j]==0; // !!!  } } if(!UP)  {  dp[pos] = ret; } return ret;}ll cal(ll x) { n=0while(x)  {  d[n++]=x%10;  x/=10; } return dfs(n-1,1).all;}int main(){ mem(dp,-1); ll n; while(cin>>n)  {  cout<<cal(n)<<endl; } return 0;}


           

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

这里写图片描述

猜你喜欢

转载自blog.csdn.net/jffyuhgv/article/details/83920781
今日推荐