动态规划——leetcode

记忆化搜索

688. 骑士在棋盘上的概率 - 力扣(LeetCode)

class Solution {
public:
    double ans1 = 0;
    double ans2 = 0;
    int dx[8] = {1, 1, -1, -1, 2, 2, -2, -2};
    int dy[8] = {2, -2, 2, -2, 1, -1, 1, -1};
    double mp[26][26][110];//从x,y,已经走了cnt步,还能出去的概率
    double knightProbability(int n, int k, int row, int column) {
        //找到所有可能
        for(int i = 0; i < 26; i ++ )
        for(int j = 0; j < 26; j ++ )
        for(int k = 0; k < 110; k ++ )
        {
            mp[i][j][k] = -1;
        }
        dfs(n, k, 0, row, column);
        return 1 - mp[row][column][0];
    }
    double dfs(int n, int k, int cnt, int x, int y)
    {
        if(x >= n || y >= n || x < 0 || y < 0) 
        {
            return 1 / pow(8, cnt);//从上一步走了1步走出边界。
        }
        if(mp[x][y][cnt] != -1)//已经知道了这个位置要的概率和
        {
            return mp[x][y][cnt];
        }
        if(cnt == k)//走了k步未走出
        {
            mp[x][y][cnt] = 0;
            return 0;
        }
        mp[x][y][cnt] = 0;//从0开始累加
        for(int i = 0; i < 8; i ++ )
        {
            int nx = x + dx[i];
            int ny = y + dy[i];
            double t = dfs(n, k, cnt + 1, nx, ny);
            mp[x][y][cnt] += t;
        }
        return mp[x][y][cnt];
    }
};

动态规划解法

class Solution {
public:
    vector<vector<int>> dirs = {{-2, -1}, {-2, 1}, {2, -1}, {2, 1}, {-1, -2}, {-1, 2}, {1, -2}, {1, 2}};

    double knightProbability(int n, int k, int row, int column) {
        vector<vector<vector<double>>> dp(k + 1, vector<vector<double>>(n, vector<double>(n)));
        for (int step = 0; step <= k; step++) {
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    if (step == 0) {
                        dp[step][i][j] = 1;
                    } else {
                        for (auto & dir : dirs) {
                            int ni = i + dir[0], nj = j + dir[1];
                            if (ni >= 0 && ni < n && nj >= 0 && nj < n) {
                                dp[step][i][j] += dp[step - 1][ni][nj] / 8;
                            }
                        }
                    }
                }
            }
        }
        return dp[k][row][column];
    }
};

689. 三个无重叠子数组的最大和 - 力扣(LeetCode)

class Solution {
public:
    int sum[20010];
    int dp[4][20010];//到i的最大值,不是以i结尾
    //有两种操作,1是不要i结尾的这个序列,那么就是从[j, i - 1]转移来的,2是要i这个序列,那么就是从[j - 1, i - k]转移过来
    vector<int> maxSumOfThreeSubarrays(vector<int>& nums, int k) {
        int n = nums.size();
        for(int i = 0; i < n; i ++ )
            sum[i + 1] = sum[i] + nums[i];
        vector<int> res;
        int ans = 0;
        for(int i = k; i <= n; i ++ )
        {
            for(int j = 1; j <= 3; j ++ )
                dp[j][i] = max(dp[j][i - 1], dp[j - 1][i - k] + sum[i] - sum[i - k]);               
            ans = max(ans, dp[3][i]);
        }
        for(int i = 3; i >= 1; i --)
        {
            for(int j = k; j <= n; j ++ )
            {
                if(dp[i][j] == ans)
                {
                    res.push_back(j - k);
                    ans -= sum[j] - sum[j - k];
                    break;
                }
            }
        }
        reverse(res.begin(), res.end());
        return res;
    }
};

375. 猜数字大小 II 题解 - 力扣(LeetCode)

记忆化搜索,超级快

class Solution {
public:
    int book[210][210];
    int getMoneyAmount(int n) {
        return dfs(1, n);
    }
    int dfs(int l, int r)
    {
        if(book[l][r] != 0) return book[l][r];
        if(l >= r) return 0;
        int res = 1e9;
        for(int i = (l + r) >> 1; i < r; i ++ )
        {
            res = min(res, max(dfs(l, i - 1), dfs(i + 1, r)) + i);
        }
        book[l][r] = res;
        return res;
    }
};

区间dp,动态规划,可以发现枚举中间点时不会低于(i + j) >> 1 , 也不会枚举最后j

class Solution {
public:
    int dp[210][210];
    int getMoneyAmount(int n) {
        memset(dp, 0x3f, sizeof(dp));
        for(int i = 1; i <= n; i ++ ) dp[i][i] = 0, dp[i][i - 1] = 0;
        for(int l = 2; l <= n; l ++ )
        for(int i = 1; i + l - 1 <= n; i ++ )
        {
            int j = i + l - 1;
            for(int k = (i + j) >> 1; k < j; k ++ )
            {
                dp[i][j] = min(dp[i][j], max(dp[i][k - 1], dp[k + 1][j]) + k);
            }
        }
        return dp[1][n];
    }
};

494. 目标和 - 力扣(LeetCode)

记忆化搜索

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int t) {
        return dfs(nums, t, 0, 0);
    }
    unordered_map<string, int> mp;
    int dfs(vector<int> & nums, int t, int u, int cur)
    {
        string key = to_string(u) + '-' + to_string(cur);//用这个标记结果
        if(mp.count(key)) return mp[key];
        if(u == nums.size()){
            if(t == cur)
                mp[key] = 1;
            else mp[key] = 0;
            return mp[key];
        }
        int left = dfs(nums, t, u + 1, cur + nums[u]);
        int right = dfs(nums, t, u + 1, cur - nums[u]);
        mp[key] = left + right;
        return mp[key];
    }
};

动态规划

class Solution {
public:
    int dp[1010];
    int findTargetSumWays(vector<int>& nums, int target) {
        //可以先全部加起来,然后01背包,然后是恰好是这个容量的方案数,因为是减,所以用0开始
        int sum = 0;
        for(auto x: nums) sum += x;
        dp[sum] = 1;//意思是全都不要的方案数为1
        
        for(int i = 0; i < nums.size(); i ++ )
            for(int j = abs(target); j + 2 * nums[i] <= sum; j ++ )
                dp[j] += dp[j + 2* nums[i]];//意思要了i则从dp[j + nums[i]]转移到dp[j]
        return dp[abs(target)];
    }
};

剑指 Offer 10- I. 斐波那契数列 - 力扣(LeetCode)

class Solution {
    const int p = 1e9 + 7;
public:
    int F[110];
    int fib(int n) {
        if(F[n] != 0) return F[n];
        if(n == 0) return 0;
        if(n == 1) return 1;
        F[n] = (fib(n - 1) + fib(n - 2)) % p;
        return  F[n];
    }
};

403. 青蛙过河 - 力扣(LeetCode)

记忆化搜索

class Solution {
public:
    unordered_map<int, bool> f;
    unordered_map<string, bool> mp;
    //f表示有没有这个石子,mp表示行不行得通
    bool canCross(vector<int>& stones) {
        //可以用上一步的k以及到达的步数标记点
        for(int i = 0; i < stones.size(); i ++ )
            f[stones[i]] = true;//所有步数表示的下标
        return dfs(stones, 0, 0);
    }
    
    int dfs(vector<int> & stones, int k, int cur)
    {
        string key = to_string(k) + "-" + to_string(cur);
        if(mp.count(key)) return mp[key];
        if(cur == stones[stones.size() - 1])//走到最后了
        {
            mp[key] = true;
            return true;
        }
        int kk[3] = {k - 1, k , k + 1};
        bool p = false;
        for(int i = 0; i < 3; i ++ )
        {
            if(kk[i] >= 1 && f[cur + kk[i]]) //有这个石子
            {
                p |= dfs(stones, kk[i], cur + kk[i]);
                if(p) break;
            }
        }
        mp[key] = p;
        return p;
    }
};

动态规划

class Solution {
public:
// state: dp[i][j] -》现在在i,到i用了j步,能否到
// dp[n][i] for i in range(0, n) if dp[n][i]
    bool canCross(vector<int>& stones) {
        int n = stones.size();
        vector<vector<bool>> dp(n, vector<bool>(n, false));
        dp[0][0] = true;
        for (int i = 1; i < n; ++i) {
            for (int j = i - 1; j >= 0; --j) {
                int k = stones[i] - stones[j];
                if (k > j + 1) break;
                dp[i][k] = dp[j][k] || dp[j][k - 1] || dp[j][k + 1];
            }
        }
        for (auto i : dp[n - 1]) if (i == true) return true;
        return false;
    }
};

576. 出界的路径数 - 力扣(LeetCode)

记忆化搜索

class Solution {
public:
    int dx[4] = {0,0,1,-1};
    int dy[4] = {1,-1,0,0};
    int mp[55][55][55];
    const int p = 1e9 + 7;

    int findPaths(int m, int n, int bu, int x, int y) {
        memset(mp, 0xcf, sizeof(mp));
        return findPath(m, n, bu, x, y);
    }
    int findPath(int m, int n, int bu, int x, int y) {
        
        if(y < 0 || x < 0 || x >=m || y >= n)
            return 1;
        if(!bu) return 0;
        if(mp[x][y][bu] != 0xcfcfcfcf) return mp[x][y][bu];
        int ans = 0;
        for(int i = 0; i < 4; i ++ )
        {
            ans += findPath(m, n, bu - 1, x + dx[i], y + dy[i]) % p;
            ans %= p;
        }
        mp[x][y][bu] = ans;
        return ans % p;
    }
};

552. 学生出勤记录 II - 力扣(LeetCode)

记忆化搜索

class Solution {
const int p = 1e9 + 7;
public:
    int mp[100010][2][3];
    int checkRecord(int n) {
        //状态表示[][][]
        memset(mp, 0xcf, sizeof(mp));
        return dfs(n, 0, 0, 0);
    }
    int dfs(int n, int cur, int A_cnt, int L_state)
    {
        if(mp[cur][A_cnt][L_state] != 0xcfcfcfcf) return mp[cur][A_cnt][L_state];
        if(cur == n)
            return 1;
        int res = 0;
        if(A_cnt < 1) //缺勤次数少于1
        res += dfs(n, cur + 1, A_cnt + 1, 0);
        res %= p;
        if(L_state < 2)//前边没有连续两天迟到
        res += dfs(n, cur + 1, A_cnt, L_state + 1);//保留两天
        res %= p;
        res += dfs(n, cur + 1, A_cnt, 0);
        mp[cur][A_cnt][L_state] = res % p;
        return mp[cur][A_cnt][L_state];
    }

};

动态规划

class Solution {
public:
    static constexpr int MOD = 1'000'000'007;

    int checkRecord(int n) {
        int dp[2][3];  // A 的数量,结尾连续 L 的数量
        memset(dp, 0, sizeof(dp));
        dp[0][0] = 1;
        for (int i = 1; i <= n; i++) {
            int dpNew[2][3];  // A 的数量,结尾连续 L 的数量
            memset(dpNew, 0, sizeof(dpNew));
            // 以 P 结尾的数量
            for (int j = 0; j <= 1; j++) {
                for (int k = 0; k <= 2; k++) {
                    dpNew[j][0] = (dpNew[j][0] + dp[j][k]) % MOD;
                }
            }
            // 以 A 结尾的数量
            for (int k = 0; k <= 2; k++) {
                dpNew[1][0] = (dpNew[1][0] + dp[0][k]) % MOD;
            }
            // 以 L 结尾的数量
            for (int j = 0; j <= 1; j++) {
                for (int k = 1; k <= 2; k++) {
                    dpNew[j][k] = (dpNew[j][k] + dp[j][k - 1]) % MOD;
                }
            }
            memcpy(dp, dpNew, sizeof(dp));
        }
        int sum = 0;
        for (int j = 0; j <= 1; j++) {
            for (int k = 0; k <= 2; k++) {
                sum = (sum + dp[j][k]) % MOD;
            }
        }
        return sum;
    }
};

87. 扰乱字符串 - 力扣(LeetCode)

class Solution {
public:
    bool isScramble(string &s1, string &s2) {
        //记忆化搜索
        return dfs(s1, s2);
    }
    unordered_map<string, bool> mp;
    bool dfs(string s1, string s2)
    {
        string key = s1 + '-' + s2;
        if(mp.count(key)) return mp[key];
        if(s1 == s2)
        {
            mp[key] = true;
            return true;
        }
        if(!check(s1, s2))
        {
            mp[key] = false;
            return false;
        }
        bool flag = false;
        int l = 0, r = s1.size() - 1;
        //l -- i   i + 1 --- r
        for(int i = 0; i < r; i ++ ){
            string x = s1.substr(l, i - l + 1);
            string y = s1.substr(i + 1, r - i);
            flag |= dfs(x, s2.substr(l, i - l + 1)) && dfs(y, s2.substr(i + 1, r - i));
            flag |= dfs(x, s2.substr(r - i + l, i - l + 1)) && dfs(y, s2.substr(l, r - i));
        }
        mp[key] = flag;
        return flag;
    }
    bool check(string &s1, string &s2) {
    	if(s1.size() != s2.size()) return false;
    	vector<int> cnt1(26,0), cnt2(26,0);
    	for(auto c : s1)
    		cnt1[c-'a']++;
    	for(auto c : s2)
    		cnt2[c-'a']++;
    	return cnt1 == cnt2;
    }
};

线性dp

467. 环绕字符串中唯一的子字符串

class Solution {
public:
    int findSubstringInWraproundString(string p) {
        vector<int> dp(26);
        int k = 0;
        for (int i = 0; i < p.length(); ++i) {
            if (i && (p[i] - p[i - 1] + 26) % 26 == 1) { // 字符之差为 1 或 -25
                ++k;
            } else {
                k = 1;
            }
            dp[p[i] - 'a'] = max(dp[p[i] - 'a'], k);
        }
        return accumulate(dp.begin(), dp.end(), 0);
    }
};

10. 正则表达式匹配 - 力扣(LeetCode)

记忆化搜索

class Solution {
public:
    string s, p;
    int m, n;
    unordered_map<int, unordered_map<int, bool>> mp; 
    bool isMatch(string _s, string _p) {
        s = _s, p = _p;
        m = s.size(), n = p.size();
        return dfs(0 ,0);
    }
    bool dfs(int i, int j) //s 到 i , p 到 j
    {
        if(j == n) return i == m;//p用完了可以结束
        if(mp[i].count(j)) return mp[i][j];

        bool res = false;
        if(j + 1 < n && p[j + 1] == '*')
        {
            if(i < m && (s[i] == p[j] || p[j] == '.'))
                res = dfs(i, j + 2) || dfs(i + 1, j + 2) || dfs(i + 1, j);//?* 匹配0个、匹配1个、匹配多个
            else 
                res = dfs(i, j + 2);
        }
        else if(i < m && (s[i] == p[j] || p[j] == '.'))
            res = dfs(i + 1, j + 1);
        mp[i][j] = res;
        return res;
    }
};

动态规划

class Solution {
public:
    bool match(int i, int j, string p, string s)
    {
        if(i == 0) return false;
        if(p[j - 1] == '.')
            return true;
        return s[i - 1] == p[j - 1];
    }
    bool isMatch(string s, string p) {
        int m = s.size();
        int n = p.size();
        vector<vector<int>> f(m + 1, vector<int>(n + 1));
        f[0][0] = true;
        for(int i = 0; i <= m; i ++ )
        for(int j = 1; j <= n; j ++ )
        {
            if(p[j - 1] == '*')
            {
                f[i][j] |= f[i][j - 2];
                if(match(i, j - 1, p, s))
                f[i][j] |= f[i - 1][j];
            }
            else
            {
                if(match(i, j, p, s))
                    f[i][j] |= f[i - 1][j - 1];
            }
        }
        return f[m][n];
    }
};

44. 通配符匹配 - 力扣(LeetCode)

class Solution {
public:
    string s, p;
    int m, n;
    unordered_map<int, unordered_map<int, bool>> mp; 
    bool isMatch(string _s, string _p) {
        if(_s == "")
        {
            _s = 'a';
            _p = _p.append("a"); 
        }
        s = _s, p = _p;
        m = s.size(), n = p.size();
        return dfs(0 ,0);
    }
    bool dfs(int i, int j) //s 到 i , p 到 j
    {
        if(j == n) return i == m;//p用完了可以结束
        if(mp[i].count(j)) return mp[i][j];

        bool res = false;
        if(i < m && p[j] == '*')
        {
            res = dfs(i, j + 1) || dfs(i + 1, j) || dfs(i + 1, j + 1);  
        }
        else if(i < m && (s[i] == p[j] || p[j] == '?'))
            res = dfs(i + 1, j + 1);
    
        if(i == m && p[j] == '*') res |= dfs(i, j + 1);
        mp[i][j] = res;
        return res;
    }
};

91. 解码方法 - 力扣(LeetCode)

class Solution {
public:
    int numDecodings(string s) {
        vector<int> dp(s.size() + 1);
        dp[0] = 1;
        for(int i = 1; i <= s.size(); i ++ )
        {
            if(s[i - 1] != '0')
            dp[i] += dp[i - 1];
            if(i > 1 && s[i - 2] != '0' && (s[i - 2] - '0') * 10 + s[i - 1] - '0' <= 26)
            dp[i] += dp[i - 2];
        }
        return dp[s.size()];
    }
};

115. 不同的子序列 - 力扣(LeetCode)

class Solution {
public:
    int numDistinct(string s, string t) {
        //dp[i][j] s到i,t到j的子序列个数
        int m = s.size(), n = t.size();
        vector<unsigned> dp(n + 1);
        dp[0] = 1;
        for(int i = 1; i <= m; i ++ )
        for(int j = n; j >= 1; j -- )
            if(s[i - 1] == t[j - 1])
                dp[j] += dp[j - 1];
        return dp[n];
    }
};

639. 解码方法 II - 力扣(LeetCode)

ONE = {'1': 1, '2': 1, '3': 1, '4': 1, '5': 1, '6': 1, '7': 1, '8': 1, '9': 1, '*': 9}
TWO = {'10': 1, '11': 1, '12': 1, '13': 1, '14': 1, '15': 1, '16': 1, '17': 1, '18': 1, '19': 1, '20': 1,
        '21': 1, '22': 1, '23': 1, '24': 1, '25': 1, '26': 1, '*0': 2, '*1': 2, '*2': 2, '*3': 2, '*4': 2,
        '*5': 2, '*6': 2, '*7': 1, '*8': 1, '*9': 1, '1*': 9, '2*': 6, '**': 15}
class Solution:
    def numDecodings(self, s: str) -> int:
        dp = 1, ONE.get(s[:1], 0)
        for i in range(1, len(s)):
            dp = dp[1], (ONE.get(s[i], 0) * dp[1] + TWO.get(s[i - 1: i + 1], 0) * dp[0]) % 1000000007
        return dp[-1]

650. 只有两个键的键盘 - 力扣(LeetCode)

class Solution {
public:
    int minSteps(int n) {
        //打印出n个A的最少操作
        vector<int> f(n + 1, n + 1); 
        f[1] = 0;
        for(int i = 2; i <= n; i ++ )
        for(int j = 1; j * j <= i; j ++)
        {
            if(i % j == 0) //从j复制过来
            {
                f[i] = min(f[i], f[j] + i / j);
                f[i] = min(f[i], f[i / j] + j);
            }
        }
        return f[n];
    }
};

1751. 最多可以参加的会议数目 II - 力扣(LeetCode)

记忆化搜索

class Solution {
public:
    vector<int> be;
    vector<vector<int>> mp;
    int maxValue(vector<vector<int>>& events, int k) {
        mp = vector<vector<int>>(events.size() + 1, vector<int>(k, INT_MIN));
        sort(events.begin(), events.end());
        for(auto t : events)
            be.push_back(t[0]);
        return dfs(0, 0, events, k);
    }
    int dfs(int cur, int sum, vector<vector<int>>& events, int k)
    {
        if(sum >= k) return 0;//sum 超了返回
        if(mp[cur][sum] != INT_MIN) return mp[cur][sum];
        if(cur == events.size() - 1) //如果能参加最后一个就返回
            return events[cur][2];
        //如果不参加这一个
        int res = 0;
        res = max(res, dfs(cur + 1, sum, events, k));//看看下一个能参加吗
        
        //如果参加这个
        int res2 = events[cur][2];
        auto it = upper_bound(be.begin() + cur, be.end(), events[cur][1]);//找到继续参加的会议
        if(it != be.end())
            res2 += dfs(it - be.begin(), sum + 1, events, k);
        
        res = max(res, res2);
        mp[cur][sum] = res;
        return res;
    }
};

动态规划(差不多快)

class Solution {
public:
    static bool cmp(const vector<int>& x, const vector<int>& y){
        return x[1] < y[1];//定义vector排序函数
    }
    int maxValue(vector<vector<int>>& events, int k) {
        int n = events.size();
        sort(events.begin(), events.end(), cmp);
        vector<vector<int>> dp(n, vector<int>(k + 1, INT_MIN));
        dp[0][0] = 0;
        dp[0][1] = events[0][2];
        for(int i = 1; i < n; i ++ ){
            //如果参加会议,二分查找前一个可以参加的会议也就是结束时间小的
            int l = 0, r = i - 1;
            int next = -1;
            while(l <= r)
            {
                int mid = (l + r) / 2;
                if(events[mid][1] < events[i][0]){
                    next = max(next, mid);
                    l = mid + 1;
                }else{
                    r = mid - 1;
                }
            }
            if(next != -1){
                for(int j = 1; j <= k; j ++ ){
                    dp[i][j] = max(dp[i][j], dp[next][j - 1] + events[i][2]);
                }
            }else{
                //没有下一个了,当前只会参加1次会议
                dp[i][1] = max(dp[i][1], events[i][2]);
            }
            //不参加会议
            for(int j = 0; j <= k; j ++ ){
                dp[i][j] = max(dp[i][j], dp[i - 1][j]);
            }
        }
        int ans = 0;
        for(int i = 1; i <= k; i ++ ){
            ans = max(ans, dp[n - 1][i]);
        }
        return ans;
    }
};

1787. 使所有区间的异或结果为零 - 力扣(LeetCode)

【宫水三叶】如何抽象成二维问题进行求解 - 使所有区间的异或结果为零 - 力扣(LeetCode)

class Solution {
private:
    // x 的范围为 [0, 2^10)
    static constexpr int MAXX = 1 << 10;
    // 极大值,为了防止整数溢出选择 INT_MAX / 2
    static constexpr int INFTY = INT_MAX / 2;
    
public:
    int minChanges(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> f(MAXX, INFTY);
        // 边界条件 f(-1,0)=0
        f[0] = 0;
        
        for (int i = 0; i < k; ++i) {
            // 第 i 个组的哈希映射
            unordered_map<int, int> cnt;
            int size = 0;
            for (int j = i; j < n; j += k) {
                ++cnt[nums[j]];
                ++size;
            }

            // 求出 t2
            int t2min = *min_element(f.begin(), f.end());

            vector<int> g(MAXX, t2min);
            for (int mask = 0; mask < MAXX; ++mask) {
                // t1 则需要枚举 x 才能求出
                for (auto [x, countx]: cnt) {
                    g[mask] = min(g[mask], f[mask ^ x] - countx);
                }
            }
            
            // 别忘了加上 size
            for(auto &x : g)
                x += size;
            f = g;
        }

        return f[0];
    }
};

LCP 07. 传递信息 - 力扣(LeetCode)

class Solution {
public:
    int dp[20][20];
    int numWays(int n, vector<vector<int>>& relation, int k) {
        //dp[][] 到达i经过j轮
        dp[0][0] = 1;
        for(int i = 1; i <= k; i ++ )
        {
            for(auto t: relation)
            {
                int a = t[0], b = t[1];
                dp[b][i] += dp[a][i - 1];
            }
        }
        return dp[n - 1][k];
    }
};

序列dp

72. 编辑距离 - 力扣(LeetCode)

十分注意dp的初始状态

class Solution {
public:
    int dp[510][510];
    int minDistance(string word1, string word2) {
        int n = word1.size(), m = word2.size();
        memset(dp, 0x3f, sizeof dp);
        dp[0][0] = 0;
        for(int i = 1; i <= n; i ++ ) dp[i][0] = i;
        for(int j = 1; j <= m; j ++ ) dp[0][j] = j;
        for(int i = 1; i <= n; i ++ )
        for(int j = 1; j <= m; j ++ )
        {
            //插入一个字符
            dp[i][j] = min(dp[i][j], dp[i - 1][j] + 1);
            //删除一个字符
            dp[i][j] = min(dp[i][j], dp[i][j - 1] + 1);
            //替换一个字符
            dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + (word1[i - 1] != word2[j - 1]));
        }
        return dp[n][m];
    }
};

354. 俄罗斯套娃信封问题 - 力扣(LeetCode)

class Solution {
public:
    int maxEnvelopes(vector<vector<int>>& envelopes) {
        //对一个进行排序,找到最长上升子序列
        sort(envelopes.begin(), envelopes.end(), [](const auto& e1, const auto& e2) {
            return e1[0] < e2[0] || (e1[0] == e2[0] && e1[1] > e2[1]);//保证如果宽度相等的话,只要第一个,后边的不要
        });
        int n = envelopes.size();
        //dp[]以i结尾的最长上升子序列的长度
        vector<int> f = {envelopes[0][1]};
        for (int i = 1; i < n; ++i) {
            if (int num = envelopes[i][1]; num > f.back()) {//如果大可以放后边
                f.push_back(num);
            }
            else {
                auto it = lower_bound(f.begin(), f.end(), num);//如果小,找到第一个大于等于它的值修改,
                *it = num;
            }
        }
        return f.size();
    }
};
//可以分情况看,如果这个数最后用上了,那么一定是一些小的数+num, 插入的很合理
//如果没有用上,也就是后边的数可能很小,那么序列的最长长度也不会改变。

树状数组

class Solution {
public:
    int g[10010];
    static bool cmp(vector<int> &a, vector<int> &b)
    {
        if(a[0] == b[0]) return a[1] > b[1];
        return a[0] < b[0]; 
    }
    int lowbit(int x){
        return x & -x;
    }
    void update(int x, int val)
    {
        for(int i = x; i <= 10000; i += lowbit(i))
        {
            g[i] = max(g[i], val);
        }
    }
    int find(int x)
    {
        int res = 0;
        for(int i = x; i > 0; i -= lowbit(i))
        {
            res = max(res, g[i]);
        }
        return res;
    }
    int maxEnvelopes(vector<vector<int>>& envelopes) {
        //排序后,如果大于的话,找到所有小于的值的最大值
        sort(envelopes.begin(), envelopes.end(), cmp);
        int n = envelopes.size();
        int ans = 0;
        int f[n];
        for(int i = 0; i < n; i ++ )
        {
            int mlast = find(envelopes[i][1] - 1);
            f[i] = mlast + 1;
            update(envelopes[i][1], f[i]);
            ans = max(ans, f[i]);
        }
        return ans;
    }
};

368. 最大整除子集 - 力扣(LeetCode)

class Solution {
public:
    vector<int> largestDivisibleSubset(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        vector<int> dp(n);
        int num = -1;
        //以i结尾的最长长度
        int p, ma = 0;
        for(int i = 0; i < n; i ++ )
        {
            dp[i] = 1;
            for(int j = 0; j < i; j ++ )
            {
                if(nums[i] % nums[j] == 0)
                dp[i] = max(dp[j] + 1, dp[i]);
            }
            if(ma < dp[i])
            {
                ma = dp[i];
                p = i;
            } 
        }
        vector<int> ans;
        while(ma >= 1)
        {
            if(num == -1)
            {
                ans.push_back(nums[p]);
                ma --;
                num = nums[p];
            }
            else
            {
                while(dp[p] != ma || num % nums[p] != 0) p --;//找到对对应结果
                ans.push_back(nums[p]);
                ma --;
                num = nums[p];
            }
        }
        return ans;
    }
};

446. 等差数列划分 II - 子序列 - 力扣(LeetCode)

引入一个弱等差序列的概念,即2及以上长度的序列,当两个序列合并时,可以计算数组的数量

class Solution {
public:
    int numberOfArithmeticSlices(vector<int> &nums) {
        int ans = 0;
        int n = nums.size();
        vector<unordered_map<long long, int>> f(n);
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                long long d = 1LL * nums[i] - nums[j];
                auto it = f[j].find(d);
                int cnt = it == f[j].end() ? 0 : it->second;
                ans += cnt;
                f[i][d] += cnt + 1;
            }
        }
        return ans;
    }
};

413. 等差数列划分 - 力扣(LeetCode)

class Solution {
public:
    int numberOfArithmeticSlices(vector<int> &nums) {
        int ans = 0;
        int n = nums.size();
        vector<int> f(n);        
        for (int i = 0; i < n; ++i) {
            f[i] = 1;
            if(i < 2) continue;
            int cnt = nums[i] - nums[i - 1] == nums[i - 1] - nums[i - 2] ? f[i - 1] : 0;
            ans += cnt;
            f[i] += cnt;
        }
        return ans;
    }
};

583. 两个字符串的删除操作 - 力扣(LeetCode)

class Solution {
public:
    int minDistance(string word1, string word2) {
        //空字符串也行
        //使[i, j] 成立的最小步数
        int n = word1.size(), m = word2.size();
        vector<vector<int>> dp(n + 1, vector<int> (m + 1));

        for(int i = 1; i <= n; i ++ ) dp[i][0] = i;
        for(int j = 1; j <= m; j ++ ) dp[0][j] = j;
        
        for(int i = 1; i <= n; i ++ )
        for(int j = 1; j <= m; j ++ )
        {
            if(word1[i - 1] == word2[j - 1])//可以不删
            {
                dp[i][j] = min({dp[i - 1][j - 1], dp[i][j - 1] + 1, dp[i - 1][j] + 1});
            }
            else//必须删
            {
                dp[i][j] = min({dp[i - 1][j - 1] + 2, dp[i][j - 1] + 1, dp[i - 1][j] + 1});
            }
        }
        return dp[n][m];
    }
};

629. K个逆序对数组 - 力扣(LeetCode)

class Solution {
public:
    static const int MOD = 1e9 + 7;
    int kInversePairs(int n, int k) {
        //恰好拥有k个逆序对
        //dp[][] i个数,恰好有 j 个逆序对
        vector<vector<int>> dp(n + 1, vector<int>(k + 1));
        dp[0][0] = 1;
        for(int i = 1; i <= n; i ++ )
        {
            //第i个数有 i 种插入方式,分别会使 逆序 增加 0 -- i - 1
            int sum = 0;
            int cnt = 0;
            for(int j = 0; j <= k; j ++ )
            {
                if(j <= i - 1)
                {
                    sum += dp[i - 1][j];
                }
                else
                {
                    sum += dp[i - 1][j];
                    sum -= dp[i - 1][cnt];
                    sum = (sum % MOD + MOD ) % MOD;
                    cnt ++;
                }
                dp[i][j] += sum;
                sum %= MOD;
                dp[i][j] %= MOD;
            }
            // for(int p = 0; p <= min(j, i - 1); p ++ )        0 --- 0   0 ---- 1
            // {                                                0 --- 0   0 ---- j     1 --- j
            //     dp[i][j] += dp[i - 1][j - p];
            //     dp[i][j] %= MOD;
            // }
        }
        return dp[n][k];
    
    }
};

673. 最长递增子序列的个数 - 力扣(LeetCode)

还可以树状数组优化

class Solution {
public:
    int findNumberOfLIS(vector<int>& nums) {
        //最长递增子序列的个数是不递增的长度
        int n = nums.size();
        vector<int> dp(n);//以i结尾的长度
        vector<int> cnt(n);//以i结尾的数量
        for(int i = 0; i < n; i ++ )
        {
            dp[i] = 1;
            cnt[i] = 1;
            for(int j = 0; j < i; j ++ )
            {
                if(nums[i] > nums[j])
                {
                    if(dp[i] == dp[j] + 1)//可以累加
                    {
                        cnt[i] += cnt[j];
                    }
                    else if(dp[i] < dp[j] + 1)
                    {
                        dp[i] = dp[j] + 1;
                        cnt[i] = cnt[j];
                    }
                }
            }
        }
        int res = *max_element(dp.begin(), dp.end());
        int ans = 0;
        for(int i = 0; i < cnt.size(); i ++ )
        {
            if(res == dp[i])
            ans += cnt[i];
        }
        return ans;
    }
};

740. 删除并获得点数 - 力扣(LeetCode)

class Solution {
public:
    int mp[10010];
    int dp[10010][2];//要还是不要这个
    int deleteAndEarn(vector<int>& nums) {
        for(auto x: nums)
            mp[x] ++;
        for(int i = 1; i <= 10000; i ++ )
        {
            dp[i][0] = dp[i - 1][1] + mp[i] * i;//要这个一定不要上一个
            dp[i][1] = max(dp[i - 1][1], dp[i - 1][0]);//不要这个
        }
        return max(dp[10000][0], dp[10000][1]);
    }
};

978. 最长湍流子数组 - 力扣(LeetCode)

class Solution {
public:
    typedef long long LL;
    int maxTurbulenceSize(vector<int>& arr) {
        //比较符号翻转
        int n = arr.size();
        vector<int> dp(n);
        dp[0] = 1;
        for(int i = 1; i < n; i ++ )
        {
            dp[i] = 1;
            if(arr[i] == arr[i - 1]) continue;
            if(dp[i - 1] <= 1) dp[i] = dp[i - 1] + 1;
            else
            {
                if((LL)(arr[i] - arr[i - 1]) * (arr[i - 2] - arr[i - 1]) > 0)
                dp[i] = dp[i - 1] + 1;
                else
                dp[i] = 2;
            }
        }
        return *max_element(dp.begin(), dp.end());
    }
};

1218. 最长定差子序列 - 力扣(LeetCode)

class Solution {
public:
    unordered_map<int, int> mp;
    int longestSubsequence(vector<int>& arr, int difference) {
        //超时
        int n = arr.size();
        vector<int> dp(n);
        for(int i = 0; i < n; i ++ )
        {
            dp[i] = 1;
            dp[i] = max(dp[i], mp[arr[i]] + 1);
            mp[arr[i] + difference] = max(mp[arr[i] + difference], dp[i]);
        }
        return *max_element(dp.begin(), dp.end());
    }
};

官方题解

class Solution {
public:
    int longestSubsequence(vector<int> &arr, int difference) {
        int ans = 0;
        unordered_map<int, int> dp;
        for (int v: arr) {
            dp[v] = dp[v - difference] + 1;
            ans = max(ans, dp[v]);
        }
        return ans;
    }
};

1473. 粉刷房子 III - 力扣(LeetCode)

class Solution {
public:
    int dp[110][22][110];
    int minCost(vector<int>& houses, vector<vector<int>>& cost, int m, int n, int target) {
        //dp[][][]  到i且i涂成j颜色组成 k个街区的最小花费
        memset(dp, 0x3f, sizeof(dp));
        if(houses[0] == 0)
        {
            for(int j = 0; j < n; j ++ )
            {
                dp[0][j][1] = cost[0][j];//涂某种颜色,产生一个街区。
            }
        }
        else dp[0][houses[0] - 1][1] = 0;//已经涂过了
        
        for(int i = 1; i < m; i ++ )//第几个房子
        {
            if(houses[i] == 0)
            {
                for(int j = 0; j < n; j ++ )//第几个颜色
                {
                    for(int k = 1; k <= i + 1; k ++ )//组成多少街区
                    {
                        for(int p = 0; p < n; p ++ )//上一个是什么颜色
                        {
                            if(p == j)//街区不变
                            {
                                dp[i][j][k] = min(dp[i][j][k], dp[i - 1][p][k] + cost[i][j]);
                            }
                            else//街区增加
                            {
                                dp[i][j][k] = min(dp[i][j][k], dp[i - 1][p][k - 1] + cost[i][j]);
                            }
                        }
                    }
                }
            }
            else
            {
                int j = houses[i] - 1;//不用涂色
                for(int k = 1; k <= i + 1; k ++ )//组成多少街区
                {
                    for(int p = 0; p < n; p ++ )//上一个是什么颜色
                    {
                        if(p == j)
                        {
                            dp[i][j][k] = min(dp[i][j][k], dp[i - 1][p][k]);
                        }
                        else
                        {
                            dp[i][j][k] = min(dp[i][j][k], dp[i - 1][p][k - 1]);
                        }
                    }
                }
            }
        }
        int ans = 1e9;
        for(int j = 0; j < n; j ++ )
        {
            ans = min(ans, dp[m - 1][j][target]);//最后是哪一种种颜色
        }
        if(ans == 1e9) return -1;
        return ans;
    }
};

1713. 得到子序列的最少操作次数 - 力扣(LeetCode)

class Solution {
public:
    int dp[1010][1010];
    int longestCommonSubsequence(string text1, string text2) {
        int m = text1.size(), n = text2.size();
        for(int i = 1; i <= m; i ++ )
        {
            for(int j = 1; j <= n; j ++ )
            {
                if(text1[i - 1] == text2[j - 1])//相等就累加
                {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                }
                else //不相等i 和 j 转移过来
                {
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        return dp[m][n];
    }
};

1713. 得到子序列的最少操作次数 - 力扣(LeetCode)

class Solution {
public:
    int minOperations(vector<int>& target, vector<int>& arr) {
        int n = target.size();
        unordered_map<int, int> pos;
        for(int i = 0; i < n; i ++ ){
            pos[target[i]] = i;
        }
        vector<int> d;
        for(int val: arr)
        {
            if(pos.count(val)){
                int idx = pos[val];
                auto it = lower_bound(d.begin(), d.end(), idx);
                if(it != d.end())
                    *it = idx;
                else 
                    d.push_back(idx);
            }
        }
        return n - d.size();
    }
};

背包dp

377. 组合总和 Ⅳ - 力扣(LeetCode)

在这里插入代码片class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        //组合个数
        //恰好为target的方案总数
        int n = nums.size();
        int dp[target + 1];
        memset(dp, 0, sizeof(dp));
        dp[0] = 1;
        for(int j = 1; j <= target; j ++ )
        for(int i = 1; i <= n; i ++ )
        {
            if(j >= nums[i - 1] && dp[j - nums[i - 1]] < INT_MAX - dp[j])
            {            
                dp[j] += dp[j - nums[i - 1]];
            }
        }
        return dp[target];
    }
};

1449. 数位成本和为目标值的最大数字 - 力扣(LeetCode)

class Solution {
public:
    //恰好target
    //成本cost
    //价值为 i + 1,而且不断增加
    //一个思路是先找到能够实现的最高位数, 然后找到这个位数的最佳方案
    int f[5500];
    string largestNumber(vector<int>& cost, int target) {
        memset(f, 0xcf, sizeof(f));
        f[0] = 0;
        for(int i = 1; i <= 9; i ++ )
        {
            for(int j = cost[i - 1]; j <= target; j ++)
            {
                f[j] = max(f[j], f[j - cost[i - 1]] + 1);//每个的价值都是1
            }
        }
        string ans = "";
        int w = target;
        for(int i = 1; i <= f[target]; i ++ )//每位都选一个数的最大值
        {
            for(int k = 9; k >= 1; k -- )//从大向小选
            {
                
                if(w - cost[k - 1] >= 0 && f[w] == f[w - cost[k - 1]] + 1)//选完这个数之后最多位数减1,则这一位选择个         
                {
                    w -= cost[k - 1];
                    ans += (char)(k + '0');
                    break;
                }
            }
        }
        if(ans == "") return "0";
        return ans;
    }
};

638. 大礼包 - 力扣(LeetCode)

class Solution {
public:
    map<vector<int>, int> mp;
    //记忆化搜索 满足需求的最小价格,使用map存储
    int shoppingOffers(vector<int>& price, vector<vector<int>>& special, vector<int>& needs) {
        int n = price.size();
        return dfs(price, needs, special, n);
    }
    // 记忆化搜索计算满足购物清单所需花费的最低价格
    int dfs(vector<int> price, vector<int> curNeeds, vector<vector<int>> & special, int n) {
        if (!mp.count(curNeeds)) {
            int minPrice = 0;
            for (int i = 0; i < n; ++i) {
                minPrice += curNeeds[i] * price[i]; // 不购买任何大礼包,原价购买购物清单中的所有物品
            }
            for (auto & x : special) {
                int specialPrice = x[n];
                vector<int> nxtNeeds;
                for (int i = 0; i < n; ++i) {
                    if (x[i] > curNeeds[i]) { // 不能购买超出购物清单指定数量的物品
                        break;
                    }
                    nxtNeeds.emplace_back(curNeeds[i] - x[i]);
                }
                if (nxtNeeds.size() == n) { // 大礼包可以购买
                    minPrice = min(minPrice, dfs(price, nxtNeeds, special, n) + specialPrice);
                }
            }
            mp[curNeeds] = minPrice;
        }
        return mp[curNeeds];
    }
};

879. 盈利计划 - 力扣(LeetCode)

class Solution {
public:
    //n名员工,选择一些工作,求出满足最小利润的方案
    //价值和容量可以互换
    //f[N][N] 价值N, 体积N的方案数
    int f[110][110];
    const int p = 1e9 + 7;
    int profitableSchemes(int n, int minProfit, vector<int>& group, vector<int>& profit) {
        int sum = 0;
        for(auto x: profit) sum += x;
        for(int i = 0; i <= n; i ++ )
            f[0][i] = 1;
        for(int i = 0; i < group.size(); i ++ )
        for(int j = minProfit; j >= 0; j -- )
        for(int k = n; k >= group[i]; k -- )
        {
            f[j][k] += f[max(0, j - profit[i])][k - group[i]];
            f[j][k] %= p;
        }
        return f[minProfit][n];
    }
};

区间dp

87. 扰乱字符串 - 力扣(LeetCode)

【宫水三叶】一题三解]

class Solution {
public:
    bool isScramble(string &s1, string &s2) {
        //记忆化搜索
        return dfs(s1, s2);
    }
    unordered_map<string, bool> mp;
    bool dfs(string s1, string s2)
    {
        string key = s1 + '-' + s2;
        if(mp.count(key)) return mp[key];
        if(s1 == s2)
        {
            mp[key] = true;
            return true;
        }
        if(!check(s1, s2))
        {
            mp[key] = false;
            return false;
        }
        bool flag = false;
        int l = 0, r = s1.size() - 1;
        //l -- i   i + 1 --- r
        for(int i = 0; i < r; i ++ ){
            string x = s1.substr(l, i - l + 1);
            string y = s1.substr(i + 1, r - i);
            flag |= dfs(x, s2.substr(l, i - l + 1)) && dfs(y, s2.substr(i + 1, r - i));
            flag |= dfs(x, s2.substr(r - i + l, i - l + 1)) && dfs(y, s2.substr(l, r - i));
        }
        mp[key] = flag;
        return flag;
    }
    bool check(string &s1, string &s2) {
    	if(s1.size() != s2.size()) return false;
    	vector<int> cnt1(26,0), cnt2(26,0);
    	for(auto c : s1)
    		cnt1[c-'a']++;
    	for(auto c : s2)
    		cnt2[c-'a']++;
    	return cnt1 == cnt2;
    }
};

375. 猜数字大小 II - 力扣(LeetCode)

class Solution {
public:
    int dp[210][210];
    int getMoneyAmount(int n) {
        memset(dp, 0x3f, sizeof(dp));
        for(int i = 1; i <= n; i ++ ) dp[i][i] = 0, dp[i][i - 1] = 0;
        for(int l = 2; l <= n; l ++ )
        for(int i = 1; i + l - 1 <= n; i ++ )
        {
            int j = i + l - 1;
            for(int k = (i + j) >> 1; k < j; k ++ )
            {
                dp[i][j] = min(dp[i][j], max(dp[i][k - 1], dp[k + 1][j]) + k);
            }
        }
        return dp[1][n];
    }
};

516. 最长回文子序列 - 力扣(LeetCode)

一开始的解

class Solution {
public:
    int dp[1010][1010];
    int longestPalindromeSubseq(string s) {
        //动态规划
        //区间i --- j 回文串的长度
        int n = s.size();
        for(int i = 0; i < n; i ++ )
            dp[i][i] = 1;
        for(int len = 2; len <= n; len ++ )
        {
            for(int i = 0; i + len - 1 < n; i ++ )
            {
                int j = i + len - 1;
                dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
                for(int k = i; k < j; k ++ )
                {
                    if(s[j] == s[k])
                    {
                        dp[i][j] = max(dp[i][j], dp[k + 1][j - 1] + 2);
                        break;//找到第一个符合要求的即可
                    }
                }
            }
        }
        return dp[0][n - 1];
    }
};

正解

class Solution {
public:
    int dp[1010][1010];
    int longestPalindromeSubseq(string s) {
        //动态规划
        //区间i --- j 回文串的长度
        int n = s.size();
        for(int i = 0; i < n; i ++ )
            dp[i][i] = 1;
        for(int len = 2; len <= n; len ++ )
        {
            for(int i = n - len; i >= 0; i --)
            {
                int j = i + len - 1;
                dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
                if(s[i] == s[j])
                {
                    dp[i][j] = max(dp[i][j], dp[i + 1][j - 1] + 2);
                }
            }
        }
        return dp[0][n - 1];
    }
};

664. 奇怪的打印机 - 力扣(LeetCode)

class Solution {
public:
    int dp[110][110];
    int strangePrinter(string s) {
        //dp[i][j] 打印i --- j 所需要的步数
        memset(dp, 0x3f, sizeof(dp));
        int n = s.size();
        for(int i = 0; i < n; i ++ ) dp[i][i] = 1;//打印一个字符用i
        for(int len = 2; len <= n; len ++ )
        {
            for(int i = 0; i + len - 1 < n; i ++ )
            {
                int j = i + len - 1;
                if(s[i] == s[j]) dp[i][j] = dp[i][j - 1];
                else
                for(int k = i; k < j; k ++ )//先打印到k再打印到j
                {
                    dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j]);
                }
            }
        }
        return dp[0][n - 1];
    }
};

877. 石子游戏 - 力扣(LeetCode)

class Solution {
public:
    bool stoneGame(vector<int>& piles) {
        int length = piles.size();
        auto dp = vector<vector<int>>(length, vector<int>(length));
        for (int i = 0; i < length; i++) {
            dp[i][i] = piles[i];
        }
        for (int i = length - 2; i >= 0; i--) {
            for (int j = i + 1; j < length; j++) {
                dp[i][j] = max(piles[i] - dp[i + 1][j], piles[j] - dp[i][j - 1]);//可以取走i或者j,剩下的人再从剩余堆中取,他们的差是当前玩家与另一个玩家的得分差
            }
        }
        return dp[0][length - 1] > 0;
    }
};

状压dp

526. 优美的排列 - 力扣(LeetCode)

class Solution {
public:
    int countArrangement(int n) {
        vector<int> f(1 << n);
        f[0] = 1;
        for (int mask = 1; mask < (1 << n); mask++) {
            int num = __builtin_popcount(mask);//放到哪里
            for (int i = 0; i < n; i++) {//那个数要放
                if (mask & (1 << i) && (num % (i + 1) == 0 || (i + 1) % num == 0)) {
                    f[mask] += f[mask ^ (1 << i)];//不放这个数的方案
                }
            }
        }
        return f[(1 << n) - 1];
    }
};

1931. 用三种不同颜色为网格涂色 - 力扣(LeetCode)

数位dp

600. 不含连续1的非负整数 - 力扣(LeetCode)

class Solution {
public:
    static const int N = 33;
    int f[N][2];
    void init()
    {
        //处理一位的情况 
        f[1][0] = 1;
        f[1][1] = 1;
        //从第二位开始处理
        for(int i = 2; i < N; i ++ )
        {
            f[i][0] = f[i - 1][0] + f[i - 1][1];// 这一位是0 ,上一位1或者0    
            f[i][1] = f[i - 1][0];// 这一位是1, 上一位必是 0 
        }
    }
    int dp(int n)
    {
        if(!n) return 1;
        int res = 0;
        vector<int> nums;//将n的各位数字摘出来
        while(n) nums.push_back(n % 2), n /= 2;
        int last = 0;//记录n的上一位
        for(int i = nums.size() - 1; i >= 0; i -- )
        {
            int x = nums[i];
            //可以有前导,因为前导0不会导致数量减少
            if(x == 1)//累加这一位是0--->x-1的情况,此时他的低位可以随便选,
                res += f[i + 1][0];
            if(last && x)break;// 判断这一位是x是否成立,成立的话可以继续遍历
            else last = x;
            if(!i) res ++; //n遍历到最后一位依然成立,这时会有一的累加
        }
        // 有前导0的部分
        // for(int i = 1; i < nums.size(); i ++ )
        //     res += f[i][1];//前边有0,后边可以任意取。 
        // res += f[1][0]; // 全是0 
        return res; 
    } 
    int findIntegers(int n) {
        init();//初始化每一位没有大小限制的答案数,此时可以有前导0,在进行dp的时候保证没有前导0即可。
        return dp(n);
    }
};

你可能感兴趣的:(LeetCode,动态规划,leetcode,算法)