数位DP

来源:互联网 发布:淘宝上有个正义哥代购 编辑:程序博客网 时间:2024/06/08 15:57

数位DP

所谓数位DP就是基于考虑数字的每一位来转移的DP,如果题目中出现求满足区间[l,r]的符合……性质的数的个数,那我们就考虑使用数位DP。
flx曰:

有三种写法:
第一种:从高位到低位,记一个flag看看有没有顶住上界。
第二种:预处理出定长度的dp值,然后对每一个上界下的不同位求一个答案然后合并。(例如1∼233,分别求出0∗∗∼1∗∗,20∗∼22∗,230∼233的答案)
第三种,类似于第一种,但不记上界,只是往下一位走时单独算一个“从此位开始不同的方案数”加进去。

看上去挺简单的。
某大佬的记忆化模板:

int dfs(int i, int s, bool e) {      if (i==-1) return s==target_s;      if (!e && f[i][s] != -1) return f[i][s];      int res = 0;      int u = e?num[i]:9;      for (int d = first?1:0; d <= u; ++d)          res += dfs(i-1, new_s(s, d), e&&d==u);      return e?res:f[i][s]=res;  }  

例题 HDU 2089 去™62

不吉利的数字为所有含有4或62的号码。例如:
62315 73418 88914
都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。 你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。

解析

裸模板,不解释

代码

#include<bits/stdc++.h>  using namespace std;  #define Memset(x,a) memset(x,a,sizeof(x))  const int INF=0x3f3f3f3f;  typedef long long LL;  typedef pair<int,int> P;  #define FOR(i,a,b) for(int i=a;i<b;i++)  int n,m,f[8][2],digit[10];  int cal_len(int x){      int sum=0;      while(x){sum++;x/=10;}      return sum;  }  void cal_digit(int x,int len){      Memset(digit,0);         FOR(i,1,len+1){          digit[i]=x%10;          x/=10;      }  }  int dfs(int len,int s,int e){      if(len==0) return 1;      if(!e&&f[len][s]!=-1) return f[len][s];     int ans=0,u=e?digit[len]:9;      for(int d=0;d<=u;d++){          if(d==4||(s&&d==2)) continue;          ans+=dfs(len-1,d==6,e&&d==u);      }      return e?ans:f[len][s]=ans;  }  int solve(int x){      int len=cal_len(x);      cal_digit(x,len);      int res=dfs(len,0,1);      return res;  }  int main(){      cin.tie(0);      ios::sync_with_stdio(false);      Memset(f,-1);      while(cin>>n>>m){                  if(!n&&!m) break;          cout<<solve(m)-solve(n-1)<<endl;      }      return 0;  }  
原创粉丝点击