一周Hard (2021.12.06-2021.12.12)

37. 解数独
利用位运算进行极致优化,某种程度上可能会减少计算?

class Solution {
public:
    
    vector<int> r;
    vector<int> c;
    vector<int> b;
    unordered_map<int, int> bit = {
        {1, 0},
        {2, 1},
        {4, 2},
        {8, 3},
        {16, 4},
        {32, 5},
        {64, 6},
        {128, 7},
        {256, 8}
    };
    int count;
    int n = 9;
    int m = 3;
    bool dfs(int x, int y, vector<vector<char>>& board) {
        if(count == 0) return true;
        int nx = x, ny = y + 1;
        if(ny >= n) ny = 0, nx += 1;
        int bp = x / m * m + y / m;
        int arr = 511 - (r[x] | c[y] | b[bp]);
        
        if(board[x][y] == '.') {
            count -= 1;
            while(arr > 0) {
                int j = arr & -arr;
                board[x][y] = '1' + bit[j];
                r[x] ^= j;
                c[y] ^= j;
                b[bp] ^= j;

                if(dfs(nx, ny, board)) return true;

                b[bp] ^= j;
                c[y] ^= j;
                r[x] ^= j;
                board[x][y] = '.';
                arr -= j;
            }
            count += 1;
        } else {
            return dfs(nx, ny, board);
        }
        return false;
    }
    
    void solveSudoku(vector<vector<char>>& board) {
        r = vector<int> (n, 0);
        c = vector<int> (n, 0);
        b = vector<int> (n, 0);
        count = n * n;
        for(int i = 0; i < n; ++i)
            for(int j = 0; j < n; ++j) 
                if(board[i][j] != '.') {
                    int v = board[i][j] - '1';
                    r[i] |= 1 << v;
                    c[j] |= 1 << v;
                    b[i / m * m + j / m] |= 1 << v;
                    count -= 1;
                }
        
        dfs(0, 0, board);
    }
};

51. N皇后
dfs入门题

class Solution {
public:
    
    vector<int> col;
    vector<int> mdiag;
    vector<int> cdiag;
    vector<vector<string>> res;
    vector<string> temp;
    int n;
    
    void dfs(int u) {
        if(u == n) {
            res.push_back(temp);
            return ;
        }
        
        for(int j = 0; j < n; ++j)
            if(!col[j] && !mdiag[j - u + n - 1] && !cdiag[u + j]) {
                col[j] = true;
                mdiag[j - u + n - 1] = true;
                cdiag[u + j] = true;
                temp[u][j] = 'Q';
                dfs(u + 1);
                temp[u][j] = '.';
                col[j] = false;
                mdiag[j - u + n - 1] = false;
                cdiag[u + j] = false;
            }
    }
    
    vector<vector<string>> solveNQueens(int _n) {
        n = _n;
        col = vector<int>(n, 0);
        mdiag = vector<int>(2 * n - 1, 0);  // 截距最低的直线是 y = x + (n - 1),所以统一向上n - 1即最大为2 * n - 2
        cdiag = vector<int>(2 * n - 1, 0);  // 截距最高的直线是 y = -x + (2 * n - 2) 所以最大为2 * n - 2
        for(int i = 0; i < n; ++i) temp.push_back(string(n, '.'));
        dfs(0);
        return res;
    }
};

689. 三个无重叠子数组的最大和
滑动窗口进阶版,甚至可以展开成 k k k个无重叠子数组,做法一致,大概就是转成数组,然后第 i i i个子数组更新时存第 i − 1 i-1 i1个子数组更新时的所有位置即可。

class Solution {
public:
    vector<int> maxSumOfThreeSubarrays(vector<int>& nums, int k) {
        int sum1 = 0, maxSum1, p1 = -1;
        int sum2 = 0, maxSum12 = 0, p2 = -1, p21 = -1;
        int sum3 = 0, maxSum123 = 0, p3 = -1;
        
        vector<int> ans;
        for(int i = 2 * k; i < nums.size(); ++i) {
            sum1 += nums[i - 2 * k];
            sum2 += nums[i - k];
            sum3 += nums[i];
            if(i >= 3 * k - 1) {
                if(sum1 > maxSum1) maxSum1 = sum1, p1 = i - (3 * k - 1);
                if(maxSum1 + sum2 > maxSum12) maxSum12 = maxSum1 + sum2, p2 = i - (2 * k - 1), p21 = p1;
                if(maxSum12 + sum3 > maxSum123) maxSum123 = maxSum12 + sum3, ans = vector<int>{p21, p2, i - (k - 1)};
                sum1 -= nums[i - (3 * k - 1)];
                sum2 -= nums[i - (2 * k - 1)];
                sum3 -= nums[i - (k - 1)];
            }
        }
        return ans;
    }
};

140. 单词拆分 II
先dp记录路径,然后走一遍所有合法路径即可。
这里可能有一个边界问题,在单词拆分这题中可以自己特判解决。
但是本题涉及到记录,就较为麻烦。
举个例子

s = "hello";
words = {"h, ello, hello"};
这样答案是:
res = {
	"hello",
	"h ello"
}


f = {1, 0, 0, 0, 1}; //如果每次是f[i] += f[pre[i]],那么就可以统计个数了
pre = {
	{-1},
	{},
	{},
	{},
	{-1, 0}
}

这里注意到有这个-1的问题,在只求是否可以组成时,不涉及到路径问题,因此没有产生问题。
但是需要记录所有合法情况时,这就产生了影响(数组负溢出)。
所以我们将串从1开始记录,同时结束点是0,这样就比较好处理

方便起见,将索引从 1 1 1开始记录,这样索引 0 0 0可以作为 d p dp dp的初始条件,初始化为 1 1 1

#define sz(x) (int)x.size()
class Solution {
private:
    vector<string> words;
    vector<int> f;
    vector<vector<int>> pre;
    vector<string> res;
    vector<int> temp;
public:

    void dfs(int u) {
        if(u == 0) {
            string ans = "";
            for(int i = (int)temp.size() - 1; i >= 0; --i) {
				ans += words[temp[i]];
				if(i - 1 >= 0) ans += " ";
			} 
            res.push_back(ans);
            return ;
        }
        
        for(int i = 0; i < pre[u].size(); ++i) {
            temp.push_back(pre[u][i]);
            dfs(u - sz(words[pre[u][i]]));
            temp.pop_back();
        }
    }
    
    vector<string> wordBreak(string s, vector<string>& _words) {
        words = _words;
        int n = s.size(), m = words.size();
        f = vector<int>(n + 1, 0);
        f[0] = 1;
        pre = vector<vector<int>>(n + 1, vector<int>());
        for(int i = 0; i < n; ++i)
            for(int j = 0; j < m; ++j) {
                if(i + sz(words[j]) <= n && s.substr(i, sz(words[j])) == words[j] && f[i]) {
                    f[i + sz(words[j])] = 1;
                    pre[i + sz(words[j])].push_back(j);
                }
            }
        
        res = vector<string>();
        temp = vector<int>();
        dfs(n);
        
        return res;
    }
};

1388. 3n块披萨
打家劫舍II的dp版本,个人理解

class Solution {
public:
    int maxSizeSlices(vector<int>& slices) {
        int n = slices.size();
        int m = n / 3;
        int res = 0;
        vector<vector<int>> f;
        
        // 问题在于当你选择第一个元素的时候,因为这是个环形序列,所以最后一个不能选了,所以你可以特判这两种即可
        
        // 必选第0个,那么第n-1个就不能选了
        f = vector<vector<int>>(n + 1, vector<int>(m + 1, 0));
        f[0][1] = slices[0];
        for(int i = 1; i + 1 < n; ++i) 
            for(int j = 1; j <= m; ++j) {
                // 不选第i个
                f[i][j] = f[i - 1][j];
                // 选第i个
                int val = slices[i];
                if(i - 2 >= 0) val += f[i - 2][j - 1];
                f[i][j] = max(f[i][j], val);
            }
        res = max(res, f[n - 2][m]);
            
        
        //不选第0个,那么答案就可以选第n-1个了
        f = vector<vector<int>>(n + 1, vector<int>(m + 1, 0));
        f[0][1] = 0;
        for(int i = 1; i < n; ++i) 
            for(int j = 1; j <= m; ++j) {
                // 不选第i个
                f[i][j] = f[i - 1][j];
                // 选第i个
                int val = slices[i];
                if(i - 2 >= 0) val += f[i - 2][j - 1];
                f[i][j] = max(f[i][j], val);
            }
        res = max(res, f[n - 1][m]);
        
        return res;
    }
};

297. 二叉树的序列化与反序列化

class Codec {
private:
	int l, r;
public:

    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        if(root == nullptr) return "null";
        
        string res = getString(root);
        queue<TreeNode*> q;
        q.push(root->left);
        q.push(root->right);
        
        while(!q.empty()) {
            auto cur = q.front(); q.pop();
            res += "," + getString(cur);
            if(cur != nullptr) q.push(cur->left), q.push(cur->right);
        }
        
        return res;
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        if(data == "" || data == "null") return nullptr;
        l = 0, r = 0;
        TreeNode* root = getTreeNode(data);
        
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty()) {
            auto cur = q.front(); q.pop();
            cur->left = getTreeNode(data);
            cur->right = getTreeNode(data);
            if(cur->left != nullptr) q.push(cur->left);
            if(cur->right != nullptr) q.push(cur->right);
        }
        
        return root;
    }
    
    TreeNode* getTreeNode(string& data) {
    	TreeNode* root = nullptr;
    	if(l >= (int)data.size()) return root;
        r = data.find(",", l);
        if(r == string::npos) r = (int)data.size();
        string val = data.substr(l, r - l);
        
        if(val != "null") root = new TreeNode(atoi(val.c_str()));
        
        l = r + 1;
        return root;
    }
    
    string getString(TreeNode* root) {
        if(root == nullptr) return "null";
        return to_string(root->val);
    }
};

5955. 摘水果
二分+前缀和
注意枚举的最左和最右即可。

class Solution {
public:
    int maxTotalFruits(vector<vector<int>>& fruits, int startPos, int k) {
        int res = 0;
        int n = fruits.size();
        vector<vector<int>> temp;
        //设置左右哨兵
        temp.push_back({-1, 0});
        for(auto& u : fruits) temp.push_back(u);
        temp.push_back({200001, 0});
        
        auto b_s = [&](int p, int flag) {
            int l = 0, r = (int)temp.size() - 1;
            while(l < r) {
                int mid = l + r >> 1;
                if(temp[mid][0] >= p + flag) r = mid;
                else l = mid + 1;
            }
            return l;
        };
        
        vector<int> pre(n + 2, 0);
        for(int i = 1; i <= n; ++i)
            pre[i] = pre[i - 1] + fruits[i - 1][1];
        
        for(int l = 0; l <= k; ++l) {
            int left = max(0, startPos - l);
            int right = min(200000, left + k - (startPos - left));
            right = max(right, startPos);
            
            // >= left
            int pleft = b_s(left, 0);
            pleft -= 1;
            
            // > right
            int pright = b_s(right, 1);
            // pright <= startPos
            pright -= 1;
            
            res = max(res, pre[pright] - pre[pleft]);
        }
        
        for(int r = 0; r <= k; ++r) {
            int right = min(200000, startPos + r);
            int left = right - (k - (right - startPos));
            left = min(left, startPos);
            left = max(0, left);
            
            // >= left
            int pleft = b_s(left, 0);
            pleft -= 1;
            
            // > right
            int pright = b_s(right, 1);
            // pright <= startPos
            pright -= 1;
            
            res = max(res, pre[pright] - pre[pleft]);
        }
        
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值