HDU 4734 数位DP
http://www.cnblogs.com/Griselda/archive/2013/11/20/3433295.html
上面那个链接讲得挺清楚,简单贴一下:
数位DP,用来学习数位DP了。
<数位DP>
所谓数位DP就是基于考虑数字的每一位来转移的DP。
例如求比456小的数,可以这么考虑,
4 5 6
4 5 (0~6)
4 (0~4) (0~9)
(0~3)(0~9) (0~9)
然后我们就可以考虑用dp[len][pre]表示长度为len,以pre开头的符合条件的数的个数。
这样就可以得到转移方程了。
而对于这道题,我们可以用dp[len][pre]表示长度为len且权值不大于pre的数。
这道题用记忆化搜索,除边界条件外记录dp[len][pre]的值,下一次发现以前已经计算过了就可以直接return;
初值:dp[len][pre] = 0;
dfs(len, pre, flag)表示求长度为len,不超过pre的所有符合条件的值。其中flag是用来控制边界的。
dfs过程中当深搜的边界,发现len < 0,pre >=0 的时候就返回1.
这里主要想讲一下为什么想到用数位DP,或者说数位DP可以解决什么问题
题目是给出一个限定值,求这个限定值下满足条件的个数。而这个值求得的过程是每个位分别运算得出来的,所以我们很自然可以每个位分别去算,一直算到最后一个位得出结果。
dp[len][pre]表示长度为len且权值不大于pre的数
#include <iostream> #include <string> #include <cstring> #include <cstdlib> #include <cstdio> #include <cmath> #include <algorithm> #include <stack> #include <queue> #include <cctype> #include <vector> #include <iterator> #include <set> #include <map> #include <sstream> using namespace std; #define mem(a,b) memset(a,b,sizeof(a)) #define pf printf #define sf scanf #define spf sprintf #define pb push_back #define debug printf("!\n") #define MAXN 1000000000 +5 #define MAX(a,b) a>b?a:b #define blank pf("\n") #define LL long long #define ALL(x) x.begin(),x.end() #define INS(x) inserter(x,x.begin()) #define pqueue priority_queue #define INF 0x3f3f3f3f #define ls (rt<<1) #define rs (rt<<1|1) int n,m; int a[20]; int dp[20][200000]; int dfs(int pos,int pre,bool flag) { if(pre<0) return 0; if(pos<0) return pre>=0; if(!flag && dp[pos][pre] != -1) return dp[pos][pre]; int e = flag?a[pos]:9; int ans = 0; for(int i = 0;i<=e;i++) { ans += dfs(pos-1,pre-i*(1<<pos),flag && i==e); } if(!flag) dp[pos][pre] = ans; return ans; } int main() { int t,kase=1,i,j,k; sf("%d",&t); mem(dp,-1); while(t--) { sf("%d%d",&n,&m); int len = 0,pre = 0,tmp=1; while(m) { a[len++] = m%10; m/=10; } while(n) { pre+= tmp * (n%10); tmp*=2; n/=10; } /* for(i=0;i<len;i++) pf("%d\n",a[i]); pf("%d %d\n",len,pre); */ pf("Case #%d: %d\n",kase++,dfs(len-1,pre,true)); } return 0; }