其他分享
首页 > 其他分享> > lc 动态规划[含有一个记忆化搜索]

lc 动态规划[含有一个记忆化搜索]

作者:互联网

1 动态规划

从背包问题开始:

最重要的是,能够用dp数组,1到3维度一般,去表示最终结果,对于具体的题目,dp[i][j]表示什么意思,将成为解答的关键

很多动态规划都可以使用带记忆化的搜索去做

2 例题

0410splitArrayMinMax 分割出最小的子数组最大值

1 题目

https://leetcode-cn.com/problems/split-array-largest-sum/

2 解题思路

参考官方解答:https://leetcode-cn.com/problems/split-array-largest-sum/solution/fen-ge-shu-zu-de-zui-da-zhi-by-leetcode-solution/

        bool lastCheck = false;
        while(st < ed) {
            x = (st + ed) >> 1; // means: floor of (st + ed)
            lastCheck = xIsLarge(x, nums, m);
            if(lastCheck) {
                ed = x; // when ed - st = 1, (st + ed) >> 1 == st
            } else {
                st = x + 1;
            }
        }
        return st;
 

class Solution {
public:
    int splitArray(vector<int>& nums, int m) {
        int n = nums.size();
        vector<int> preSum = { 0 };
        for(int i = 0; i < n; ++i) {
            preSum.push_back(preSum.back() + nums[i]);
        }
        // // dp[i][j] means: res of: nums[:i] to be splited in j's segments
        // // dp[i][j] = max {dp[k][j-1], sum[k+1, i] | j <= k <= i - 1}
        // vector<vector<int>> dp(n+1, vector<int>(m+1, INT_MAX));
        
        // dp[1][1] = nums[0];
        // for(int i = 1; i <= n; ++i) {
        //     for(int j = 1; j <= min(m, i); ++j) {
        //         if(j == 1) {
        //             dp[i][1] = preSum[i] - preSum[0];
        //             continue;
        //         }
        //         int tmpMaxMin = 0;
        //         for(int k = j - 1; k < i; ++k) {
        //             tmpMaxMin = max(dp[k][j-1], preSum[i] - preSum[k]);
        //             dp[i][j] = min(dp[i][j], tmpMaxMin);
        //         }
        //     }
        // }
        // return dp[n][m];
        
        // binsearch x as the min max res
        int st = *max_element(nums.begin(), nums.end());
        int ed = preSum[n];
        int x = -1;


        bool lastCheck = false;
        while(st < ed) {
            x = (st + ed) >> 1;
            lastCheck = xIsLarge(x, nums, m);
            if(lastCheck) {
                ed = x; // when ed - st = 1, (st + ed) >> 1 == st
            } else {
                st = x + 1;
            }
        }
        
        // at last, st == ed
        return st;
    }

    bool xIsLarge(int x, vector<int>& nums, int m) {
        int cnt = 1;
        int curSum = 0;
        for(int i = 0; i < nums.size(); ++i) {
            if(curSum + nums[i] > x)  {
                ++cnt;
                curSum = nums[i];
            } else {
                curSum += nums[i];
            }
        }
        // cout << ">> x/cnt is" << x << "/" << cnt << endl;
        return cnt <= m;
    }
};

0403frogCrossRiver 青蛙过河

1 题目

https://leetcode-cn.com/problems/frog-jump/

2 解题思路

class Solution {
public:
    bool canCross(vector<int>& stones) {
        int n = stones.size();
        // jump to i, and last jump dis
        vector<vector<bool>> jump(n , vector<bool>(n, false));
        jump[0][0] = true;

        // the i th jump len <= i
        for(int i = 1; i < n; ++i) {
            if(stones[i] - stones[i - 1] > i) {
                return false;
            }
        }

        // dp, from j jump to i
        bool res = false;
        for(int i = 1; i < n; ++i) {
            for(int j = 0; j < i; ++j) {
                int k = stones[i] - stones[j];
                // cout << j << " -> " << i << " 's dis: " << k << endl;
                if(k > j + 1) {
                    continue;
                }
                // cout << jump[j].size() << " / " << k + 1 << " jump[j][k] || jump[j][k-1] || jump[j][k+1] " << jump[j][k] << jump[j][k-1] << jump[j][k+1] << endl;
                jump[i][k] = jump[j][k] || jump[j][k-1] || jump[j][k+1];
                if(i == n - 1 && jump[i][k]) {
                    res = true;
                }
            }
        }
        return res;
    }
};

0488zumaGame 祖玛游戏

1 题目

https://leetcode-cn.com/problems/zuma-game/

2 解题思路

“RRYGGYYRRYYGGYRR”

“GGBBB”

class Solution {
public:
    int allBallsCnt = -1;
    map<string, int> memo;

    int findMinStep(string board, string hand) {
        // simulate this game
        int res = 0;
        allBallsCnt = hand.size();
        res = bfs(board, hand);
        return res == INT_MAX ? -1 : res;
    }
    int bfs(string board, string hand) {
        // space mustn't be eliminated! it's neutig!
        if(memo.find(board + " " + hand) != memo.end()) {
            return memo[board + " " + hand];
        }

        if(0 == board.size()) {
            return allBallsCnt - hand.size();
        }
        if(0 == hand.size()) {
            return INT_MAX;
        }

        int useRes = INT_MAX;
        string lastTarBallStr = "";
        for(int k = 0; k < hand.size(); ++k) {
            string nextHand = hand.substr(0, k) + hand.substr(k + 1);
            string tarBallStr = hand.substr(k, 1);

            // case1: cut the same ball
            if(tarBallStr == lastTarBallStr) {
                continue;
            }

            // use this char, find put pos
            for(int i = 0; i <= board.size(); ++i) {
                // case2: only insert at the start of str with same chars
                if(i > 0 && board[i - 1] == hand[k]) {
                    continue;
                }

                // case3: only put when cur is equal current || when cur is not equal to two continuous same chars
                if(i < board.size() && board[i] == hand[k] || \
                i > 0 && board[i] == board[i-1] && hand[k] != board[i-1]) {
                    string tmpBoard1 = board;
                    tmpBoard1.insert(i, tarBallStr);
                    // reduce repeat balls
                    reduceRepeat(tmpBoard1);

                    // put to tarBall left and right
                    int lRes = bfs(tmpBoard1, nextHand);

                    useRes = min(lRes, useRes);
                }
            }
            
            lastTarBallStr = tarBallStr;
        }
        memo[board + " " + hand] = useRes;
        return useRes;
    }

    inline void reduceRepeat(string& board) {
        int idx = 0;
        // cout << "reducing " << board << endl;
        while(board.length() > 0 && idx < board.length()) {
            int st = idx, cur = st;
            char head = board[st];
            while(++cur < board.length() && board[cur] == head) {
            }
            if(cur - st >= 3) {
                board.erase(st, cur - st);
                idx = 0;
            } else {
                idx = cur;
            }
        }
        // cout << "after redu " << board << endl;
    }

};

0552checkRecord 学生出勤记录

1 题目

https://leetcode-cn.com/problems/student-attendance-record-ii/

2 解题思路

class Solution {
public:

    int bigInt = 1000000007;
    int checkRecord(int n) {
        if(n <= 2) {
            return n == 1 ? 3 : 8;
        }
        
        // we can use A to interrupt the LLL, so we calculate A after only PL
        // // n's day without 'A'
        // vector<vector<long long>> dp(n + 1, vector<long long>(3, 0));
        
        // // dp[i][j], j means end with n's L
        // dp[1][0] = 1; dp[1][1] = 1; dp[1][2] = 0;


        // for(int i = 2; i <= n; ++i) {
        //     // end with p
        //     dp[i][0] = dp[i - 1][0] + dp[i - 1][1] + dp[i - 1][2];
            
        //     // ent with l
        //     dp[i][1] = (dp[i - 1][0]) % bigInt;

        //     // end with ll
        //     dp[i][2] = dp[i - 1][1] % bigInt;
        //     cout << i << " th: P/L: " << dp[i][1] << " " << dp[i][0] << endl;
        // }

        // // when there is a A:
        // long long res = 0;
        // res += ((dp[n][0] + dp[n][1]) % bigInt + dp[n][2]) % bigInt;
        // res += (((dp[n-1][0] + dp[n-1][1]) % bigInt + dp[n-1][2]) % bigInt * n) % bigInt;
       

        // n's day without 'A'
        vector<vector<vector<long long>>> dp(n + 1, vector<vector<long long>>(2, vector<long long>(3, 0)));
        
        // dp[i][j], j means end with n's L
        dp[0][0][0] = 1;

        for(int i = 1; i <= n; ++i) {
            // end with p
            for(int j = 0; j < 2; ++j) {
                for(int k = 0; k <= 2; ++k) {
                    dp[i][j][0] = (dp[i][j][0] + dp[i - 1][j][k]) % bigInt;
                }
            }

            // end with a
            for(int k = 0; k <= 2; ++k) {
                dp[i][1][0] = (dp[i][1][0] + dp[i-1][0][k]) % bigInt;
            }
            
            // ent with l
            for(int j = 0; j < 2; ++j) {
                for(int k = 1; k <= 2; ++k) {
                    dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j][k-1]) % bigInt;
                }
            }
        }

        int res = 0;

        for(int j = 0; j < 2; ++j) {
            for(int k = 0; k < 3; ++k) {
                res = (res + dp[n][j][k]) % bigInt;
            }
        }


        return res;
    }
};

0600countUncontinous1 不含连续1的非负整数

1 题目

https://leetcode-cn.com/problems/non-negative-integers-without-consecutive-ones/

2 解题思路

class Solution {
public:
    int findIntegers(int n) {
        int tmp = n;
        int bit = -1;
        vector<int> bits = {};

        while(tmp > 0) {
            bits.push_back(tmp & 1);
            tmp = tmp >> 1;
        }

        int k = bits.size();
        vector<vector<int>> dp(k, vector<int>(2, 0));

        if(k <= 1) {
            return 2;
        }

        dp[0][0] = 1; // mainly for the last bit is 1
        dp[0][1] = 0;

        dp[1][0] = 1;
        dp[1][1] = 1;

        for(int len = 2; len <= k - 1; ++len){
            dp[len][0] = dp[len - 1][1] + dp[len - 1][0];
            dp[len][1] = dp[len - 1][0];
            // cout << "dp " << len << "/0,1 = " << dp[len][0] << "," << dp[len][1] << endl;
        }

        int lastOneBitIdx = k - 1;

        // assume the biggest bit starts with 0
        int res = dp[lastOneBitIdx][0] + dp[lastOneBitIdx][1];
        // cout << "assume biggest bit = 0's res : " << res << endl;

        bool mySelfOk = true;
        while(lastOneBitIdx > 0) {
            int nextOneBitIdx = lastOneBitIdx - 1;
            cout << ">> last/next: " << lastOneBitIdx << "/" << nextOneBitIdx << endl;
            cout << "bits[next] " << bits[nextOneBitIdx] << endl;

            // find next one bit
            while(bits[nextOneBitIdx] != 1) {
                cout << "not 1 bit: " << nextOneBitIdx << endl;
                -- nextOneBitIdx;
                if(nextOneBitIdx == -1) {
                    return res + mySelfOk;
                }
            }

            cout << "last/next: " << lastOneBitIdx << "/" << nextOneBitIdx << endl;
            if(lastOneBitIdx - nextOneBitIdx < 2) {
                // view the bits[nextOneBitIdx] as 0
                res += dp[nextOneBitIdx][0] + dp[nextOneBitIdx][1];
                mySelfOk = false;
 
                // cout << "+ dp " << nextOneBitIdx << "," << 0 << " break!!!" << endl;
                break;
            } else {
                // view the bits[nextOneBitIdx] as 0
                res += dp[nextOneBitIdx][0] + dp[nextOneBitIdx][1];
                lastOneBitIdx = nextOneBitIdx;
                // cout << "+ dp " << nextOneBitIdx << "," << 0 << endl;
                // cout << "lastOneBitIdx is " << lastOneBitIdx << endl;
                // cout << "curRes = " << res;

            }
        }

        cout << "me ok: " << mySelfOk << endl;

        return res + mySelfOk;
    }
};

标签:lc,int,res,st,记忆,board,搜索,hand,dp
来源: https://blog.csdn.net/cxy_hust/article/details/123619164