數位dp從lv1到lv2
上一篇windy數的記憶化搜尋做法裡,開了一個dp[pos][pre][lim][zero]陣列,這個陣列可以減少一維,把zero那維去掉,但是在dfs的引數裡還是要保留zero的
考慮到在記憶化搜尋過程中,大部分數都是zero==0,只有少數數是zero==1,所以我們只對zero==0的情況做記憶化處理
int dp[12][10][2]; // dp[pos][pre][lim] int dfs(int pos, int pre, bool lim,bool zero) { if (~dp[pos][pre][lim]&&!zero) return dp[pos][pre][lim]; if (!pos) { if (!zero) return dp[pos][pre][lim] = 1;//記憶化 else return 1;//不做記憶化處理 } int res = 0; int r = lim ? b[pos] : 9; int l = zero ? 1 : 0; for (int i = l; i <= r; i++) { if (abs(i - pre) < 2) continue; res += dfs(pos - 1, i, lim && i == r, 0);//不是最高位,可以取0 } if (!zero) return dp[pos][pre][lim] = res;//記憶化 else return res;//不做記憶化處理 }
同樣,lim這一維也是可以刪掉的
int dp[12][10]; // dp[pos][pre] int dfs(int pos, int pre, bool lim,bool zero) { if (~dp[pos][pre]&&!zero&&!lim) return dp[pos][pre]; if (!pos) { if (!zero&&!lim) return dp[pos][pre] = 1;//記憶化 else return 1;//不做記憶化處理 } int res = 0; int r = lim ? b[pos] : 9; int l = zero ? 1 : 0; for (int i = l; i <= r; i++) { if (abs(i - pre) < 2) continue; res += dfs(pos - 1, i, lim && i == r, 0);//不是最高位,可以取0 } if (!zero&&!lim) return dp[pos][pre] = res;//記憶化 else return res;//不做記憶化處理 }
那麼pre這一維呢,思考一下,感覺不行
我試了一下,發現樣例是過了,交一發試試,結果全wa了,看來對於這題來說還是不能刪掉這一維
因為windy數涉及到pos的前一位數對pos這位數的影響,所以dp數組裡和dfs的引數裡都要有pre
而在有些其他題目裡,比如 不要62,你甚至可以只開一維!! 用一維記憶化搜尋!!
那麼減維有什麼用呢,減維雖然減少了空間,但記憶化的東西少了,就增加了時間,在空間本來就很充足的情況下,減維是不合適的
如果空間不夠,可以考慮減維
有時候,dfs的引數裡也可以刪掉zero這一項,比如題目說前導零的數也是合法的,或者把題目看成是前導零的數也是合法的,並不影響最終的輸出
gym102452J Junior Mathematician 2019ICPC 香港
這題我一開始想出來的是開一個dp[5000][10][60][60][60][2][2] (dp[pos][pre][su][f(x)%m][x%m][lim][zero]) 的陣列
後來研究了好久題解才知道,這題把lim和zero兩維刪去了,同時這題不用考慮pre,dfs和dp數組裡都沒pre,同時dfs的引數也可以刪掉zero這一項
根據具體的題意可以把 f(x) % m 和 x % m 合併成 ( f(x) - x ) % m
這樣dp陣列就變成了dp[5000][60][60] (dp[pos][su][mmm] , mmm = ( f(x) - x ) % m)
#include<iostream> #include<algorithm> #include<cstring> using namespace std; const long long MOD = 1e9 + 7; long long dp[5003][61][61]; int pp[5003]; int m; int b[5003]; int tot = 0; long long dfs(int pos, int su, int mmm,bool lim) { if (pos < 0) return 0; if (~dp[pos][su][mmm]&&!lim) return dp[pos][su][mmm]; if (!pos && !lim) { if(!mmm) return dp[pos][su][mmm] = 1; return dp[pos][su][mmm] = 0; } if (!pos) { if (!mmm) return 1; return 0; } int l, r; l = 0; r = lim ? b[pos] : 9; long long res = 0; for (int j = l; j <= r; j++) { res += dfs(pos - 1, (su + j) % m, ((mmm - j * pp[pos] + j * su) % m + m) % m, lim && j == r); } res %= MOD; if (!lim) return dp[pos][su][mmm] = res; else return res; } long long qiu(string s,int len) { if (len < 2) return 0; for (register int i = 0; i <= len; i++) { for (int j = 0; j <= m; j++) { for (int k = 0; k <= m; k++) { dp[i][j][k] = -1; } } } for (int i = 1; i <= len; i++) { b[i] = s[len - i + 1] - '0'; } for (; !b[len]; len--); long long res = 0; return dfs(len, 0, 0, 1);//刪去zero這一引數後,就只需從第len位開始搜 } int main() { int T; pp[1] = 1; string A, B; cin >> T; while (T--) { cin >> A >> B >> m; for (int i = 2; i <= 5000; i++) pp[i] = pp[i - 1] * 10 % m; int lena = A.length(); int lenb = B.length(); A = " " + A; B = " " + B; A[lena]--; for (int i = lena; i; i--) { if (A[i] < '0') { A[i] += 10; A[i - 1]--; } else break; } cout << (qiu(B,lenb) - qiu(A,lena)+MOD)%MOD << endl; } return 0; }
附上這題dfs的引數裡沒刪去zero的程式碼
long long dfs(int pos, int su, int mmm, bool lim, bool zero) { if (pos < 0) return 0; if (~dp[pos][su][mmm] && !lim && !zero) return dp[pos][su][mmm]; if (!pos && !lim && !zero) { if (!mmm) return dp[pos][su][mmm] = 1; return dp[pos][su][mmm] = 0; } if (!pos) { if (!mmm) return 1; return 0; } int l, r; l = zero ? 1 : 0; r = lim ? b[pos] : 9; long long res = 0; for (int j = l; j <= r; j++) { res += dfs(pos - 1, (su + j) % m, ((mmm - j * pp[pos] + j * su) % m + m) % m, lim && j == r, 0); } res %= MOD; if (!lim && !zero) return dp[pos][su][mmm] = res; else return res; } long long qiu(string s, int len) { if (len < 1) return 0; for (register int i = 0; i <= len; i++) { for (int j = 0; j <= m; j++) { for (int k = 0; k <= m; k++) { dp[i][j][k] = -1; } } } for (int i = 1; i <= len; i++) { b[i] = s[len - i + 1] - '0'; } for (; !b[len]; len--); long long res = 0; res = dfs(len, 0, 0, 1, 1); for (int i = len - 1; i > 1; i--) { res += dfs(i, 0, 0, 0, 1);//沒刪去zero這一引數,相當於要考慮前導零,就要加上這些 } res %= MOD; return res; }