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
i−1个子数组更新时的所有位置即可。
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;
}
};