dp总结篇
dp,直接递推和记忆化搜索
View Code
View Code
记忆化搜索
leetcode 1553 吃掉N个橘子的最少天数
思路:除2或除3总比减一划算,所以先减掉余数再除。由于N很大,需要记忆化搜索;由于N很大,不能用数组,可以用unordered_map
class Solution { public: // vector<int>dp; unordered_map<int, int>mp; int dfs(int n) { if(mp[n]) return mp[n]; int& ans = mp[n]; if(n == 0) return ans=-1; // 特殊,1需要1次,0最小需要-1次 return ans = min(dfs(n/3)+n%3, dfs(n/2)+n%2)+1; } int minDays(int n) { // dp.resize(n+1, -1); return dfs(n); } };
数位DP
leetcode 902 最大为N的数组组合
思路:数位dp,逐位考虑,同时记录前面的一些必要信息。pre记录是否已经小于,flag记录前面是否是全0. (细节挺多,每种情况都要考虑到,到最后发现很多情况能合并)
class Solution { public: typedef long long ll; ll qpow(ll a, ll b) { ll ret = 1; while(b) { if(b&1) ret *= a; b >>= 1; a *= a; } return ret; } ll dfs(vector<string>& digits, vector<int>& limits, int pos, bool pre, bool flag) { // // cout << "pos: " << pos << endl; int n = digits.size(), m = limits.size(); if(pos >= m) return !flag; int limit = limits[pos]; ll ret = 0; // 前面小 if(pre) { // 填0 if(flag) ret += dfs(digits, limits, pos+1, pre, flag); // 不填0 ret += qpow(n, m-pos); } else { // 前面等 int i = 0; for(;i < n;i++) if(stoi(digits[i]) >= limit) break; // 填limit if(i < n && stoi(digits[i]) == limit) ret += dfs(digits, limits, pos+1, pre, false); // 填0 if(flag) ret += dfs(digits, limits, pos+1, true, flag); // 填(0, limit) ret += i*qpow(n, m-pos-1); } return ret; } int atMostNGivenDigitSet(vector<string>& digits, int n) { int tmp = n; vector<int>limits; while(tmp) { limits.insert(limits.begin(), tmp%10); tmp /= 10; } // for(int num : limits) cout << num << " "; ll ans = dfs(digits, limits, 0, false, true); return ans; } };
个性签名:时间会解决一切