這周開始刷數位DP,在網上找到一份神級數位DP模板,作起題目來爽歪歪。html
http://www.cnblogs.com/jffifa/archive/2012/08/17/2644847.htmlios
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位待填數);ide
s爲以前數字的狀態(若是要求後面的數知足什麼狀態,也能夠再記一個目標狀態t之類,for的時候枚舉下t);spa
e表示以前的數是不是上界的前綴(即後面的數可否任意填)。設計
for循環枚舉數字時,要注意是否能枚舉0,以及0對於狀態的影響,有的題目前導0和中間的0是等價的,但有的不是,對於後者能夠在dfs時再加一個狀態變量z,表示前面是否所有是前導0,也能夠看是不是首位,而後外面統計時候枚舉一下位數。It depends.code
因而關鍵就在怎麼設計狀態。固然作多了以後狀態一眼就能夠瞄出來。htm
HDU2098 不要62blog
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 }