数位DP专题
这周开始刷数位DP,在网上找到一份神级数位DP模板,做起题目来爽歪歪。
http://www.cnblogs.com/jffifa/archive/2012/08/17/2644847.html
1 int dfs(int i, int s, bool e) { 2 if (i==-1) return s==target_s; 3 if (!e && ~f[i][s]) return f[i][s]; 4 int res = 0; 5 int u = e?num[i]:9; 6 for (int d = first?1:0; d <= u; ++d) 7 res += dfs(i-1, new_s(s, d), e&&d==u); 8 return e?res:f[i][s]=res; 9 }
f为记忆化数组;
i为当前处理串的第i位(权重表示法,也即后面剩下i+1位待填数);
s为之前数字的状态(如果要求后面的数满足什么状态,也可以再记一个目标状态t之类,for的时候枚举下t);
e表示之前的数是否是上界的前缀(即后面的数能否任意填)。
for循环枚举数字时,要注意是否能枚举0,以及0对于状态的影响,有的题目前导0和中间的0是等价的,但有的不是,对于后者可以在dfs时再加一个状态变量z,表示前面是否全部是前导0,也可以看是否是首位,然后外面统计时候枚举一下位数。It depends.
于是关键就在怎么设计状态。当然做多了之后状态一眼就可以瞄出来。
HDU2098 不要62

1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <utility> 6 #include <vector> 7 #include <queue> 8 using namespace std; 9 #define INF 0x3f3f3f3f 10 #define maxn 15 11 int dp[maxn][2],num[maxn]; 12 int new_s(int s, int d) 13 { 14 if (d == 6) return 1; 15 else 16 { 17 return 0; 18 } 19 } 20 int dfs(int i, int s, bool e) { 21 if (i == -1) return 1; 22 if (!e && ~dp[i][s]) return dp[i][s]; 23 int res = 0; 24 int u = e ? num[i] : 9; 25 for (int d = 0; d <= u; ++d) 26 { 27 if (d==4) continue; 28 if (s&&d==2) continue; 29 res += dfs(i - 1, new_s(s, d), e && d == u); 30 } 31 return e ? res : dp[i][s] = res; 32 } 33 int cal(int n) 34 { 35 int cnt = 0; 36 while (n) 37 { 38 num[cnt++] = n % 10; 39 n /= 10; 40 } 41 return dfs(cnt - 1, 0,1); 42 } 43 int main() 44 { 45 int l, r; 46 memset(dp, -1, sizeof(dp)); 47 while (scanf("%d%d", &l, &r) != EOF&&l+r) 48 { 49 printf("%d\n", cal(r) - cal(l -1)); 50 } 51 return 0; 52 }
HDU3555 Bomb
和上题差不多

1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <utility> 6 #include <vector> 7 #include <queue> 8 using namespace std; 9 #define INF 0x3f3f3f3f 10 #define maxn 30 11 typedef long long LL; 12 LL dp[maxn][3]; 13 int num[maxn]; 14 int new_s(int s, int d) 15 { 16 if (s == 2) return 2; 17 if (s == 1 && d == 9) return 2; 18 if (d == 4) return 1; 19 return 0; 20 21 } 22 LL dfs(int i, int s, bool e) 23 { 24 if (i == -1) return s == 2; 25 if (!e&&~dp[i][s]) return dp[i][s]; 26 LL ret = 0; 27 int u = e ? num[i] : 9; 28 for (int d = 0; d <= u; d++) 29 ret += dfs(i - 1, new_s(s, d), e&&d == u); 30 return e ? ret : dp[i][s] = ret; 31 32 } 33 LL cal(LL n) 34 { 35 int cnt = 0; 36 while (n) 37 { 38 num[cnt++] = n % 10; 39 n /= 10; 40 } 41 return dfs(cnt - 1, 0, 1); 42 } 43 int main() 44 { 45 int T; 46 scanf("%d", &T); 47 memset(dp, -1, sizeof(dp)); 48 while (T--) 49 { 50 LL n; 51 scanf("%I64d", &n); 52 printf("%I64d\n", cal(n)); 53 } 54 return 0; 55 }
BZOJ1026 windy数
注意前导0

1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <utility> 6 #include <vector> 7 #include <queue> 8 using namespace std; 9 #define INF 0x3f3f3f3f 10 #define maxn 30 11 typedef long long LL; 12 LL dp[maxn][10]; 13 int num[maxn]; 14 LL dfs(int i, int s, bool e,int pre) 15 { 16 if (i == -1) return s == 1; 17 if (!e&&~dp[i][pre]&&s) return dp[i][pre]; 18 LL ret = 0; 19 int u = e ? num[i] : 9; 20 for (int d = 0; d <= u; d++) 21 if(!s||abs(pre-d)>=2) 22 { 23 ret += dfs(i - 1, s||d>0, e&&d == u, d); 24 } 25 if (!e&&s)dp[i][pre] = ret; 26 return ret; 27 } 28 29 LL cal(LL n) 30 { 31 int cnt = 0; 32 while (n) 33 { 34 num[cnt++] = n % 10; 35 n /= 10; 36 } 37 return dfs(cnt - 1, 0, 1,11); 38 } 39 int main() 40 { 41 LL x,y; 42 memset(dp, -1, sizeof(dp)); 43 while (scanf("%lld%lld", &x,&y)!=EOF) 44 { 45 printf("%lld\n", cal(y)-cal(x-1)); 46 } 47 return 0; 48 }
HDU3652 B-number
加一维,记录余数

1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <utility> 6 #include <vector> 7 #include <queue> 8 using namespace std; 9 #define INF 0x3f3f3f3f 10 #define maxn 30 11 typedef long long LL; 12 LL dp[maxn][15][3]; 13 int num[maxn]; 14 int new_s(int s, int d) 15 { 16 if (s == 2) return 2; 17 if (s == 1 && d == 3) return 2; 18 if (d == 1) return 1; 19 return 0; 20 } 21 LL dfs(int i, int s, bool e,int r) 22 { 23 if (i == -1) 24 { 25 if ((s== 2) && (r== 0)) return 1; 26 else return 0; 27 } 28 if (!e&&~dp[i][r][s]) return dp[i][r][s]; 29 LL ret = 0; 30 int u = e ? num[i] : 9; 31 for (int d = 0; d <= u; d++) 32 { 33 ret += dfs(i - 1,new_s(s,d) , e&&d == u, (r*10+d)%13); 34 } 35 return e ? ret : dp[i][r][s] = ret; 36 } 37 38 LL cal(LL n) 39 { 40 int cnt = 0; 41 while (n) 42 { 43 num[cnt++] = n % 10; 44 n /= 10; 45 } 46 return dfs(cnt - 1, 0, 1,0); 47 } 48 int main() 49 { 50 LL n; 51 memset(dp, -1, sizeof(dp)); 52 while (scanf("%I64d",&n)!=EOF) 53 { 54 printf("%I64d\n", cal(n)); 55 } 56 return 0; 57 }
HDU3943 K-th Nya Number
需要二分答案,还有就是注意区间范围是[P+1,Q],被坑了好多发,还有我很不解的就是,memset放在最外面就会WA,每输入一组案例清空一次就AC了。。坑爹。。

1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <utility> 6 #include <vector> 7 #include <queue> 8 using namespace std; 9 #define INF 0x3f3f3f3f 10 #define maxn 25 11 typedef long long LL; 12 LL dp[maxn][maxn][maxn]; 13 LL P, Q, x, y; 14 int num[maxn]; 15 LL dfs(int i, int sx, int sy, bool e) 16 { 17 if (i == -1) 18 { 19 if (sx == x&&sy == y) return 1; 20 else return 0; 21 } 22 if (!e&&~dp[i][sx][sy]) return dp[i][sx][sy]; 23 LL ret = 0; 24 int u = e ? num[i] : 9; 25 for (int d = 0; d <= u; d++) 26 { 27 if (sx == x&&d == 4) continue; 28 if (sy == y&&d == 7) continue; 29 int a, b; 30 a = sx; b = sy; 31 if (d == 4) a++; 32 if (d == 7) b++; 33 ret += dfs(i - 1, a, b, e&&d == u); 34 } 35 return e ? ret : dp[i][sx][sy] = ret; 36 } 37 LL cal(LL n) 38 { 39 int cnt = 0; 40 while (n) 41 { 42 num[cnt++] = n % 10; 43 n /= 10; 44 } 45 return dfs(cnt - 1, 0, 0, 1); 46 } 47 LL Bin(LL k) 48 { 49 LL l, r, mid, ans,ret; 50 ans = 0; 51 ret = cal(P); 52 l = P+1; r = Q; 53 while (l <= r) 54 { 55 mid = (l + r) >> 1; 56 if (cal(mid) - ret>= k) 57 { 58 ans = mid; 59 r = mid - 1; 60 } 61 else l = mid + 1; 62 } 63 return ans; 64 } 65 int main() 66 { 67 int T,kase=0; 68 scanf("%d", &T); 69 70 while (T--) 71 { 72 int n; 73 memset(dp, -1, sizeof(dp)); 74 scanf("%I64d%I64d%I64d%I64d", &P, &Q, &x, &y); 75 scanf("%d", &n); 76 printf("Case #%d:\n", ++kase); 77 while (n--) 78 { 79 LL k; 80 scanf("%I64d",&k); 81 LL ans = Bin(k); 82 if (ans) 83 printf("%I64d\n", ans); 84 else printf("Nya!\n"); 85 } 86 } 87 return 0; 88 }
POJ3208 Apocalypse Someday
做法和上题一样,需要注意的是它是必须有连续的三个6,还有就是二分的上界尽量大。。

1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <utility> 6 #include <vector> 7 #include <queue> 8 using namespace std; 9 #define INF 0x3f3f3f3f 10 #define maxn 30 11 typedef long long LL; 12 LL dp[maxn][4]; 13 int num[maxn]; 14 int new_d(int s, int d) 15 { 16 if (s == 3) return 3; 17 int st = s; 18 if (d == 6) s++; 19 return st==s?0:s; 20 } 21 LL dfs(int i, int s,bool e) 22 { 23 if (i == -1) return s == 3; 24 if (!e&&~dp[i][s]) return dp[i][s]; 25 LL ret = 0; 26 int u = e ? num[i] : 9; 27 for (int d = 0; d <= u; d++) 28 { 29 ret += dfs(i - 1, new_d(s,d),e&&d == u); 30 } 31 return e ? ret : dp[i][s] = ret; 32 } 33 LL cal(LL n) 34 { 35 int cnt = 0; 36 while (n) 37 { 38 num[cnt++] = n % 10; 39 n /= 10; 40 } 41 return dfs(cnt - 1, 0, 1); 42 } 43 LL Bin(LL k) 44 { 45 LL l, r, mid, ans,ret; 46 ans = 0; 47 l = 666, r = 100000000000LL; 48 while (l <= r) 49 { 50 mid = (l + r) >> 1; 51 if (cal(mid)>= k) 52 { 53 ans = mid; 54 r = mid - 1; 55 } 56 else l = mid + 1; 57 } 58 return ans; 59 } 60 int main() 61 { 62 int T; 63 scanf("%d", &T); 64 while (T--) 65 { 66 LL k; 67 memset(dp, -1, sizeof(dp)); 68 scanf("%I64d", &k); 69 printf("%I64d\n", Bin(k)); 70 } 71 return 0; 72 }
SPOJ BALNUM Balanced Numbers
刚开始一直不知道该怎么记录前面的状态,搜了下解题报告,用的是三进制来表示前面的状态(此题的精华就是这里吧。。),因为状态总数为3^10,因此也不大。

1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <utility> 6 #include <vector> 7 #include <queue> 8 using namespace std; 9 #define INF 0x3f3f3f3f 10 #define maxn 66666 11 typedef long long LL; 12 LL dp[25][maxn]; 13 int num[25]; 14 int cnt[10]; 15 void go(int s) 16 { 17 for (int i = 0; i < 10; i++) 18 { 19 cnt[i] = s % 3; 20 s /= 3; 21 } 22 } 23 int new_s(int s, int d) 24 { 25 go(s); 26 if (cnt[d] == 0) cnt[d] = 1; 27 else 28 cnt[d] = 3 - cnt[d]; 29 int base = 1; 30 s = 0; 31 for (int i = 0; i < 10; i++) 32 { 33 s += base * cnt[i]; 34 base *= 3; 35 } 36 return s; 37 } 38 int check(int s) 39 { 40 go(s); 41 for (int i = 0; i < 10; i++) 42 { 43 if ((i & 1) && (cnt[i] == 1)) return 0; 44 if (!(i & 1) && (cnt[i] == 2))return 0; 45 } 46 return 1; 47 } 48 LL dfs(int i, int s, bool e,int zero) 49 { 50 if (i == -1) return check(s); 51 if (!e&&~dp[i][s]) return dp[i][s]; 52 LL ret = 0; 53 int u = e ? num[i] : 9; 54 for (int d = 0; d <= u; d++) 55 { 56 ret += dfs(i - 1, zero&&d==0?0:new_s(s, d), e&&d == u,zero&&d==0); 57 } 58 return e ? ret : dp[i][s] = ret; 59 } 60 LL cal(LL n) 61 { 62 int cnt = 0; 63 while (n) 64 { 65 num[cnt++] = n % 10; 66 n /= 10; 67 } 68 return dfs(cnt - 1, 0, 1,1); 69 } 70 int main() 71 { 72 int T; 73 scanf("%d", &T); 74 memset(dp, -1, sizeof(dp)); 75 while (T--) 76 { 77 LL x, y; 78 scanf("%lld%lld", &x, &y); 79 printf("%lld\n", cal(y) - cal(x - 1)); 80 } 81 return 0; 82 }
SPOJ MYQ10 Mirror Number
每个数字只可能是0,1,8,区间比较大0 <= a<=b <= 10^44,所以输入要用字符串,一般我们求答案都是:cal(b)-cal(a-1),但此题是字符串,因此需要特殊下a是不是Mirror Number,还被坑了好久的就是,由于是字符串输入,最高位的下标是0。。然后我没有反转过来。。找了好久才发现。。用一个数组记录前面选的数(之前的状态),只要第一个非0位确定就可以知道回文串的长度,也就知道回文串中心的位置,然后从中心更低的位置开始判断是不是回文。前导0也需要注意

1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <utility> 6 #include <vector> 7 #include <queue> 8 using namespace std; 9 #define INF 0x3f3f3f3f 10 #define maxn 50 11 typedef long long LL; 12 LL dp[maxn][maxn]; 13 int num[maxn],tmp[maxn]; 14 LL dfs(int i, int len, bool e,int zero) 15 { 16 if (i == -1) return 1; 17 if (!e&&~dp[i][len]) return dp[i][len]; 18 LL ret = 0; 19 int u = e ? num[i] : 9; 20 for (int d = 0; d <= u; d++) 21 { 22 if (d!=0&&d!=1&&d!=8) continue; 23 if (zero) 24 { 25 tmp[i] = d; 26 ret += dfs(i - 1, len - !d, e&&d == u, zero&& d == 0); 27 } 28 else 29 { 30 int mid = len / 2; 31 int fg = i < mid ? 1 : 0; 32 if (fg) 33 { 34 if (tmp[len - i - 1] == d) 35 ret += dfs(i - 1, len, e&&d == u, zero); 36 } 37 else 38 { 39 tmp[i] = d; 40 ret += dfs(i - 1, len, e&&d == u, zero); 41 } 42 } 43 } 44 return e ? ret : dp[i][len] = ret; 45 } 46 LL cal(char *s) 47 { 48 int cnt = strlen(s); 49 for (int i = 0; i < cnt; i++) num[cnt-i-1] = s[i] - '0'; 50 return dfs(cnt - 1, cnt, 1,1); 51 } 52 int ok(char *s) 53 { 54 int len = strlen(s); 55 for (int i = 0; i < len; i++) 56 if (s[i] != '0'&& s[i] != '8'&&s[i] != '1') return 0; 57 for (int i = 0; i < len / 2; i++) 58 if (s[i] != s[len - i - 1]) return 0; 59 return 1; 60 } 61 int main() 62 { 63 int T; 64 scanf("%d", &T); 65 memset(dp, -1, sizeof(dp)); 66 while (T--) 67 { 68 char x[maxn], y[maxn]; 69 scanf("%s%s", x, y); 70 printf("%lld\n", cal(y) - cal(x)+ok(x)); 71 } 72 73 return 0; 74 }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步