给你一个下标从 0 开始、大小为 m x n 的二进制矩阵 matrix ;另给你一个整数 numSelect,表示你必须从 matrix 中选择的 不同 列的数量。
如果一行中所有的 1 都被你选中的列所覆盖,则认为这一行被 覆盖 了。
形式上,假设 s = {c1, c2, …, cnumSelect} 是你选择的列的集合。对于矩阵中的某一行 row ,如果满足下述条件,则认为这一行被集合 s 覆盖:
对于满足 matrix[row][col] == 1 的每个单元格 matrix[row][col](0 <= col <= n - 1),col 均存在于 s 中,或者
row 中 不存在 值为 1 的单元格。
你需要从矩阵中选出 numSelect 个列,使集合覆盖的行数最大化。
返回一个整数,表示可以由 numSelect 列构成的集合 覆盖 的 最大行数 。
输入:matrix = [[0,0,0],[1,0,1],[0,1,1],[0,0,1]], numSelect = 2
输出:3
解释:
图示中显示了一种覆盖 3 行的可行办法。
选择 s = {0, 2} 。
- 第 0 行被覆盖,因为其中没有出现 1 。
- 第 1 行被覆盖,因为值为 1 的两列(即 0 和 2)均存在于 s 中。
- 第 2 行未被覆盖,因为 matrix[2][1] == 1 但是 1 未存在于 s 中。
- 第 3 行被覆盖,因为 matrix[2][2] == 1 且 2 存在于 s 中。
因此,可以覆盖 3 行。
另外 s = {1, 2} 也可以覆盖 3 行,但可以证明无法覆盖更多行。
示例 2:
输入:matrix = [[1],[0]], numSelect = 1
输出:2
解释:
选择唯一的一列,两行都被覆盖了,因为整个矩阵都被覆盖了。
所以我们返回 2 。
提示:
m == matrix.length
n == matrix[i].length
1 <= m, n <= 12
matrix[i][j] 要么是 0 要么是 1
1 <= numSelect <= n
二进制枚举(灵神题解):
class Solution {
public:
int maximumRows(vector<vector<int>> &mat, int numSelect) {
int m = mat.size(), n = mat[0].size();
vector<int> mask(m);
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
mask[i] |= mat[i][j] << j;
}
}
int ans = 0;
for (int subset = 0; subset < (1 << n); subset++) {
if (__builtin_popcount(subset) == numSelect) {
int covered_rows = 0;
for (int row : mask) {
if ((row & subset) == row) {
covered_rows++;
}
}
ans = max(ans, covered_rows);
}
}
return ans;
}
};
而我的代码时间和空间占用都太高了,虽然是比较直观的:
class Solution {
public:
int maximumRows(vector<vector<int>>& mat, int cols) {
int m = mat.size(), n = mat[0].size();
if(cols == n) return m;
int ans = 0;
vector<int> tmp;
for(int i = 0; i < m; i++) {
int num = 0;
for(int j = 0; j < n; j++) {
if(mat[i][j]) num++;
}
tmp.push_back(num);
}
for(int i = 1; i < (1<<n); i++) {
vector<int> v;
int k = i;
for(int j = 0; j < n && k > 0; j++) {
if(k & 1) v.push_back(j);
k >>= 1;
}
if(v.size() == cols) {
int res = 0;
for(int t = 0; t < m; t++) {
if(tmp[t] > cols) continue;
int sum = 0;
for(int q = 0; q < v.size(); q++) {
if(mat[t][v[q]]) sum++;
}
if(sum == tmp[t]) res++;
}
ans = max(ans, res);
}
}
return ans;
}
};
有 n 个人排成一个队列,从左到右 编号为 0 到 n - 1 。给你以一个整数数组 heights ,每个整数 互不相同,heights[i]
表示第 i 个人的高度。
一个人能 看到 他右边另一个人的条件是这两人之间的所有人都比他们两人 矮 。更正式的,第 i 个人能看到第 j 个人的条件是 i < j 且 min(heights[i], heights[j]) > max(heights[i+1], heights[i+2], ..., heights[j-1])
。
请你返回一个长度为 n 的数组 answer
,其中 answer[i]
是第 i 个人在他右侧队列中能 看到 的 人数 。
输入:heights = [10,6,8,5,11,9]
输出:[3,1,2,1,1,0]
解释:
第 0 个人能看到编号为 1 ,2 和 4 的人。
第 1 个人能看到编号为 2 的人。
第 2 个人能看到编号为 3 和 4 的人。
第 3 个人能看到编号为 4 的人。
第 4 个人能看到编号为 5 的人。
第 5 个人谁也看不到因为他右边没人。
示例 2:
输入:heights = [5,1,2,3,10]
输出:[4,1,1,1,0]
提示:
n == heights.length
1 <= n <= 105
1 <= heights[i] <= 105
heights 中所有数 互不相同 。
虽然是hard题,但是实际上是中等题的难度(但我还是看了题解,太菜了),很明显的单调栈解决,及时去掉无用数据:
class Solution {
public:
vector<int> canSeePersonsCount(vector<int>& heights) {
int n = heights.size();
vector<int> stack;
vector<int> res(n, 0);
for (int i = n - 1; i >= 0; i--) {
int h = heights[i];
while (!stack.empty() && stack.back() < h) {
stack.pop_back();
res[i] += 1;
}
if (!stack.empty()) {
res[i] += 1;
}
stack.push_back(h);
}
return res;
}
};
给你一个链表的头 head ,每个结点包含一个整数值。
在相邻结点之间,请你插入一个新的结点,结点值为这两个相邻结点值的 最大公约数 。
请你返回插入之后的链表。
两个数的 最大公约数 是可以被两个数字整除的最大正整数。
输入:head = [18,6,10,3]
输出:[18,6,6,2,10,1,3]
解释:第一幅图是一开始的链表,第二幅图是插入新结点后的图(蓝色结点为新插入结点)。
- 18 和 6 的最大公约数为 6 ,插入第一和第二个结点之间。
- 6 和 10 的最大公约数为 2 ,插入第二和第三个结点之间。
- 10 和 3 的最大公约数为 1 ,插入第三和第四个结点之间。
所有相邻结点之间都插入完毕,返回链表。
示例 2:
输入:head = [7]
输出:[7]
解释:第一幅图是一开始的链表,第二幅图是插入新结点后的图(蓝色结点为新插入结点)。
没有相邻结点,所以返回初始链表。
提示:
链表中结点数目在 [1, 5000] 之间。
1 <= Node.val <= 1000
这个题灵神的题解实在是太优雅了,这里就不贴出我的菜鸡代码了orz
遍历链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode *insertGreatestCommonDivisors(ListNode *head) {
for (auto cur = head; cur->next; cur = cur->next->next) {
cur->next = new ListNode(gcd(cur->val, cur->next->val), cur->next);
}
return head;
}
};