HDU 4734 F(x) 数位DP
数位DP 越来越顺了。但是无脑了一下,对于每组数据都删除记忆,10000组数据档当然TLE了。
题意:求0~b中有多少个数X, 其f(x)<=f(a)
分析:
设g[i][j]表示长度为i的数,距离f(a)还剩j的量。
g[i][j]=Σ(g[i-1][j-k*2^(i-1)] (0<=k<=9)
然后就是套路了。
#include <stdio.h> #include <string.h> int g[20][10000]; int f(int x) { int sum=0; int y=1; while (x>0) { sum+=y*(x%10); x/=10; y=y*2; } return sum; } int dp(int i,int j) { if (j<0) { return 0; } if (g[i][j]!=-1) { return g[i][j]; } if (i==0) { if (j<0) { return 0; } else { return g[i][j]=1; } } int ans=0; for (int k=0;k<=9;k++) { if ((j-k*(1<<(i-1)))>=0) ans+=dp(i-1,j-k*(1<<(i-1))); } return g[i][j]=ans; } int main() { int i,j,k; int t; int len; int a,b; int last; int digit[100]; int cas=0; scanf("%d",&t); memset(g,-1,sizeof(g)); while (t--) { scanf("%d%d",&a,&b); last=f(a); len=0; b++; while (b>0) { digit[++len]=b%10; b/=10; } int ans=0; for (i=len;i>=1;i--) { for (j=0;j<digit[i];j++) { ans+=dp(i-1,last-j*(1<<(i-1))); } last-=digit[i]*(1<<(i-1)); } printf("Case #%d: %d\n",++cas,ans); } return 0; }
这里有个浓缩版。 from CXL。
#include <cstdio> #include <cstring> int dp[12][10000]; int num[30]; int getf(int x) { int ans = 0; int len = 0; while (x) { ans += (x%10)*(1<<(len)); x/=10; len++; } return ans; } int dfs(int i, int f, bool isQuery) { if (f < 0) return 0; int &nowdp = dp[i][f]; if (!isQuery && ~nowdp) return nowdp; if (i == 0) { return 1; } int end = isQuery?num[i]:9; /*这里不一样哦!*/ int ans = 0; for (int j = 0; j <= end; j++) { int nextf = f-j*(1<<(i-1)); /*这里不一样哦!*/ ans += dfs(i-1, nextf, isQuery && j==end); } if (!isQuery) nowdp = ans; return ans; } int cal(int a, int x) { int len = 0; if (x == 0) { num[++len] = 0; } else { while (x) { num[++len] = x%10; x/=10; } } return dfs(len, getf(a), true); /*这里不一样哦!*/ } int main() { int t; scanf("%d", &t); memset(dp, -1, sizeof(dp)); int cas = 1; while (t--) { printf("Case #%d: ", cas++); int a, b; scanf("%d%d", &a, &b); printf("%d\n", cal(a,b)); } return 0; }