數位DP 學習筆記2
阿新 • • 發佈:2018-12-24
題目HDU 4734 F(x):
題目大意是給你兩個數A,B,定義F(A)= 每個數位的數 * 2 ^ (位數 - 1)。求 0 - B 區間裡的 F(x) <= F(A) 的數字的個數。
一個數位DP的做法(TLE):
#include <bits/stdc++.h> using namespace std; typedef long long ll; ll a[1005], FA, poww[15], dp[15][10005]; ll dfs(ll pos, ll lim, ll hav) { if(hav > FA)return 0; if(pos == -1)return 1; if(lim == 0 && dp[pos][hav] != -1)return dp[pos][hav]; ll len = lim ? a[pos] : 9, ans = 0, i; for(i = 0; i <= len; i++) { if(hav + i * poww[pos] <= FA) ans += dfs(pos - 1, lim && (i == len), hav + i * poww[pos]); } if(lim == 0)dp[pos][hav] = ans; return ans; } ll slove(ll n, ll m) { FA = 0; ll pos = 0, k = 1, ans; while(n) { FA += k * (n % 10); n /= 10; k *= 2; } while(m) { a[pos++] = m % 10; m /= 10; } ans = dfs(pos - 1, 1, 0); } int main() { ll t, n, m, i, j, k = 1, ca = 1; for(i = 0; i <= 13; i++)poww[i] = k, k *= 2; scanf("%lld", &t); while(t--) { scanf("%lld %lld", &n, &m); memset(dp, -1, sizeof(dp)); ll ans = slove(n, m); printf("Case #%lld: %lld\n", ca++, ans); } return 0; }
儘管加了記憶化搜尋,但還是超時了,然後分析發現:
每次都要 memset(dp),用了很多時間,然後想辦法優化它。
如果我們把擁有價值改變一下,變成剩餘價值,即不再是它有多少,而是它還能加多少價值,這樣,它的邊界不再是可變的F(A),而是固定的值(0)。這樣,便可以只memset(dp)一次,不需要每組都初始化了。
#include <bits/stdc++.h> using namespace std; typedef long long ll; ll a[15]; ll dp[15][200005]; ll po[15], maxx; ll F(ll x) { ll pos = 0, ans = 0, num, pi = 1; while(x) { num = x % 10; x /= 10; ans += num * pi; pi *= 2; } return ans; } ll dfs(ll pos, ll sta, ll lim) { if(sta < 0)return 0; if(pos == -1)return 1; if(lim == 0 && dp[pos][sta] != -1)return dp[pos][sta]; ll len = lim ? a[pos] : 9; ll ans = 0; for(ll i = 0; i <= len; i++) { ll w = po[pos] * i; if(sta - w < 0)continue; else { ans += dfs(pos - 1, sta - w, lim && i == a[pos]); } } if(lim == 0)dp[pos][sta] = ans; return ans; } ll slove(ll x) { ll pos = 0; while(x) { a[pos++] = x % 10; x /= 10; } ll ans = dfs(pos - 1, maxx, 1); return ans; } int main() { ll t, k, i, j, m, n; k = 1; for(i = 0; i <= 15; i++)po[i] = k, k *= 2; scanf("%lld", &t); for(ll q = 0; q < 15; q++) { for(ll w = 0; w < 200005; w++)dp[q][w] = -1; } for(i = 1; i <= t; i++) { scanf("%lld %lld", &n, &m); maxx = F(n); //cout << maxx << endl; ll ans = slove(m); if(0 <= maxx)ans++; printf("Case #%lld: %lld\n",i, ans - 1); } return 0; }
總結:遇到超時問題時可以試著轉換方法,反過來考慮。