数位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