数位dp

来源:互联网 发布:犹他大学世界排名 知乎 编辑:程序博客网 时间:2024/06/05 02:36

最近做了几道数位dp的题,写篇博客留个纪念。


一开始不太会写数位dp,然后上网看了几篇大牛的博客,自己也刷了几道题,发现其中具有一定的套路。

首先明确数位dp的作用——求某个数值区间中具有某种特征的数值的个数。

然后便是用深搜对所有的数进行一个搜索,其中加入了一个记忆化搜索的过程。


int dp[pos][state][...];                      int a[size];int solve( int n ){int cnt = 0;while( n ){a[cnt++] = n%10;n /= 10;}}
int dfs( int pos , int state , ... , int limit )    //limit为true,该数位上的数受a[i]的限制,否则取值可以为0到9{         if( pos == -1 && ..... ) return 1;else return 0;                       //符合所有要求的条件,返回1;否则返回0;if( !limit && dp[pos][state][...] != -1 ) return dp[pos][state][...];   int up = (limit?a[pos]:9);           //获取该数位的上界int ret = 0;for( int i = 0 ; i <= up ; ++i ){............                 ret += dfs( pos-1 , ... , ... ,limit&&(a[pos]==i) );}if( !limit ) dp[pos][state][...] = ret;   //记忆化搜索return ret;}




上述代码中的state为记录数的某种状态,state可以有多种。

hdu2089:给定一个数的区间,求区间内的数不含有4和62的个数

#include <stdio.h>#include <stdlib.h>#include <string.h>using namespace std;  typedef long long ll;   int dp[20][2];    //dp[pos][state] pos为数位,state为1表示前面的那个数为6;int a[20];        //记录需要搜索的数int dfs( int pos , int state , int limit )    //limit为true,该数位上的数受a[i]的限制,否则取值可以为0到9{         if( pos == -1 ) return 1;if( !limit && dp[pos][state] != -1 ) return dp[pos][state];   int up = (limit?a[pos]:9);    //获取该数位的上界int ret = 0;for( int i = 0 ; i <= up ; ++i ){if( i == 4 ) continue;if( state == 1 && i == 2 ) continue;ret += dfs( pos-1 , i == 6 , limit&&(a[pos]==i) );}if( !limit ) dp[pos][state] = ret;       //记忆化搜索return ret;}int solve( int n ){int cnt = 0;while( n ){a[cnt++] = n%10;n /= 10;}return dfs( cnt-1 , 0 , true );}int main(){int a,b;while( ~scanf("%d%d",&a,&b) ){memset( dp , -1 , sizeof(dp) );if( a == 0 && b == 0 ) break;int L1 = solve(b);int L2 = solve(a-1);printf("%d\n",L1-L2);}return 0;}

hdu3652:求数中出现13且是13倍数的个数

#include <cstdio>  #include <string>  #include <cmath>  #include <iostream>  #include <stdio.h>#include <stdlib.h>#include <string.h>using namespace std;  typedef long long ll;   int dp[15][20][2][2];      //dp[pos][mod][lead][flag] pos为层次,   //mod为%13的余数,lead记录前导是否为1,flag记录是否出现连续的13int a[15];                 //记录数位的值//lead为true表示有前导为1,flag为true表示出现了连续的13int dfs( int pos ,  int mod  , int lead , int flag , bool limit )   {         if( pos == -1 ){if( !mod  && flag ) return 1;else return 0;}if( !limit && dp[pos][mod][lead][flag] != -1 ) return dp[pos][mod][lead][flag];int up = ( limit ? a[pos] : 9 );int ans = 0;for( int i = 0 ; i <= up ; ++i ){//int s = i*pow( 10 , pos );//int t = (s%13+mod)%13;int t = (mod*10+i)%13;ans += dfs( pos-1 , t , i == 1 , flag || (lead&&(i==3)) , limit&&(i==a[pos]) );}if( !limit ) dp[pos][mod][lead][flag] = ans;return ans;}int solve( int n  ){int cnt = 0;while( n ){a[cnt++] = n%10;n /= 10;}return dfs( cnt-1 , 0 , 0 , 0 , true );}int main(){memset(dp,-1,sizeof(dp));  int N;while( ~scanf("%d",&N) ){printf("%d\n",solve(N));}return 0;}





                                             
0 0
原创粉丝点击