有些数位dp题目真心难用递推,递推貌似很难想,不过搜索相对来讲是比较好设计的,现在可以考虑用搜索,如何搜索?,记忆优化肯定必须的,像dp一样设置状态。
两个例题解答如何用记忆优化解决此类问题。
hdu 3709 平衡数
故名思意,这题要求以某个点为中心点其他各个位到这个为力矩和为0的数。
不多说,举个例子 4139 以3为中心,那么左边2*4+1*1 。右边:1*9 刚好左右相等。如何设计搜索来搜索状态呢?
我们可以考虑枚举中心点,那么对每个位的数上下波动,那么搜索基本就成型了。
状态设计这样:
dp[len][pos][res] 长度为len,中心点为pos结果为res的状态数。果断记忆搜索
#include<iostream> #include<algorithm> #include<stdlib.h> #include<string.h> #include<stdio.h> #include<math.h> #include<string> #include<vector> #include<queue> #include<list> using namespace std; #define ep 1e-9 #define oo 0x3f3f3f3f typedef __int64 lld; #define maxn 2005 lld dp[20][20][maxn]; int bit[20]; lld dfs(int len, int pos, int sum, int f) { if (!len) return sum == 0 ? 1 : 0; if (sum < 0) return 0; if (!f && dp[len][pos][sum] != -1) return dp[len][pos][sum]; int dig = f ? bit[len] : 9; lld res = 0; for (int i = 0; i <= dig; i++) { res += dfs(len - 1, pos, sum + (len - pos)*i, f && i == dig); } if (!f) dp[len][pos][sum] = res; return res; } lld Dp(lld x) { int len = 0; while (x) { bit[++len] = x % 10; x /= 10; } lld ans = 0; for (int i = len; i >= 1; i--) ans += dfs(len, i, 0, 1); return ans - len + 1; } int main() { lld n, m; int T; scanf("%d", &T); while (T--) { memset(dp, -1, sizeof dp); scanf("%I64d %I64d", &m, &n); printf("%I64d\n", Dp(n) - Dp(m - 1)); } return 0; } /* */
第二道例题:hdu4722 好数,求各位和可以整除10的个数
和上题一样,状态设为:
dp[pos][sum] pos对应的位置何为sum的状态数
/* -------------------------------------------------------------------------- dp[i][j+2][0] = max { dp[i][j+2][0] , dp[i][j-k][0] + dp[son][k][0] } dp[i][j+1][1] = max { dp[i][j+1][1] , dp[i][j-k][0] + dp[son][k][1] } dp[i][j+2][1] = max { dp[i][j+2][1] , dp[i][j-k][1] + dp[son][k][0] } -------------------------------------------------------------------------- */ #include<iostream> #include<algorithm> #include<stdlib.h> #include<string.h> #include<stdio.h> #include<math.h> #include<string> #include<vector> #include<queue> #include<list> using namespace std; #define ep 1e-9 #define oo 0x3f3f3f3f typedef __int64 lld; #define maxn 2005 lld dp[20][maxn]; int bit[20]; lld dfs(int pos, int sum, int f) { if (!pos) return sum % 10 == 0 ? 1 : 0; if (!f&&dp[pos][sum] != -1) return dp[pos][sum]; int dig = f ? bit[pos] : 9; lld res = 0; for (int i = 0; i <= dig; i++) { res += dfs(pos - 1, sum + i, f&&i == dig); } if (!f) dp[pos][sum] = res; return res; } lld Dp(lld x) { if (x < 0) return 0; int len = 0; while (x) { bit[++len] = x % 10; x /= 10; } return dfs(len, 0, 1); } int main() { lld n, m; int T; scanf("%d", &T); for (int cas = 1; cas <= T; cas++) { memset(dp, -1, sizeof dp); scanf("%I64d %I64d", &m, &n); printf("Case #%d: %I64d\n", cas, Dp(n) - Dp(m - 1)); } return 0; } /* */