Q1 有序三元组中的最大值I
-
解题思路:
- 三重循环暴力
- 初始化答案为ans
- 枚举i、j、k三个下标,分别计算三元组的值,如果大于ans则更新ans
-
解题代码:
class Solution {
public:
long long maximumTripletValue(vector<int>& nums) {
long long ans = 0;
int n = nums.size();
for(int i = 0; i < n; i++)
{
for(int j = i+1; j < n; j++)
{
for(int k = j+1; k < n; k++)
ans = max(ans, ((long long)nums[i] - nums[j]) * nums[k]);
}
}
return ans;
}
};
Q2 有序三元组中的最大值II
-
解题思路:
- 枚举j的位置,可以得到的最大分数是(j左侧最大值 - nums[j]) * j右侧最大值
-
解题代码:
class Solution {
public:
long long maximumTripletValue(vector<int>& nums) {
long long ans = 0;
/*
枚举j
(左侧最大值 - nums[j]) * 右侧最大值
*/
int n = nums.size();
vector<int> rightMin(n, 0), rightMax(n, 0);
rightMax[n-1] = 0;
for(int i = n-2; i >= 0; i--)
rightMax[i] = max(rightMax[i+1], nums[i+1]);
long long leftMax = nums[0];
for(int j = 1; j < n; j++)
{
ans = max(ans, (leftMax - nums[j]) * rightMax[j]);
leftMax = max(leftMax, (long long)nums[j]);
}
return ans;
}
};
- 另一种思路:
- 枚举k
- 需要维护nums[i] - nums[j]的最大值 max_diff
- 把当前枚举数当作 nums[j],还需要知道左侧的最大值pre_max
- 维护的方式类似Leetcode121.买卖股票的最佳时机
- 解题代码:
class Solution {
public:
long long maximumTripletValue(vector<int>& nums) {
long long ans = 0;
long long max_diff = 0, pre_max = 0;
for(auto num : nums)
{
//先当作nums[k]
ans = max(ans, max_diff * num);
//再当作nums[j]
max_diff = max(max_diff, pre_max - num);
//再当作nums[i]
pre_max = max(pre_max, (long long)num);
}
return ans;
}
};
Q3 无限数组的最短子数组
-
解题思路:
- 从infinite_nums中找出满足元素和等于target的子数组存在两种情况:
- 子数组在原始数组内部
- 子数组横跨了多个原始数组,可以表示成 原数组一个后缀和 + (0 - n)个原始数组和 + 原数组一个前缀和
- 先求原始数组所有元素和sum
- 将原始数组重复两遍,在新的数组中找是否有子数组sub其和满足(target % sum) ,则和为target的子数组长度为sub长度 + target/sum * 原始数组长度
- 从infinite_nums中找出满足元素和等于target的子数组存在两种情况:
-
解题代码:
class Solution {
public:
int minSizeSubarray(vector<int>& nums, int target) {
int ans = INT_MAX;
int n = nums.size();
long long sum = accumulate(nums.begin(), nums.end(), 0ll);
int cnt = target / sum;
target %= sum;
if(target == 0)
return cnt * n;
sum = 0;
unordered_map<int, int> um;
um[0] = -1;
for(int i = 0; i < 2*n; i++)
{
sum += nums[i%n];
if(um.count(sum - target))
ans = min(ans, i - um[sum-target] + cnt * n);
um[sum] = i;
}
return ans < INT_MAX ? ans : -1;
}
};
- 可以使用滑动窗口优化到需要O(1)空间
- 解题代码:
class Solution {
public:
int minSizeSubarray(vector<int>& nums, int target) {
int ans = INT_MAX;
int n = nums.size();
long long sum = accumulate(nums.begin(), nums.end(), 0ll);
int left = 0;
long long s = 0;
for(int right = 0; right < 2*n; right++)
{
s += nums[right % n];
while(s > target % sum)
{
s -= nums[left % n];
left += 1;
}
if(s == target % sum)
ans = min(ans, right-left+1);
}
if(ans == INT_MAX)
return -1;
return ans + (target / sum) * n;
}
};
Q4 有向图访问计数
-
解题思路:
- 基环树(内向基环树)
- 先用拓扑排序把环以外的树枝全部删去,此时剩下的就是基环部分,这里注意可能存在多个基环
- 遍历每一个基环上的每个点,该点可到达的节点数为其所在的基环长度
- 以该点为根进行dfs,其子树上的节点可以到达的节点数为子树上节点到该节点距离 + 基环长度
-
解题代码:
class Solution {
public:
vector<int> countVisitedNodes(vector<int>& edges) {
int n = edges.size();
vector<int> deg(n, 0);
vector<vector<int>> g(n);
for(int i = 0; i < n; i++)
{
deg[edges[i]] += 1;
g[edges[i]].push_back(i);
}
//拓扑排序
queue<int> q;
for(int i = 0; i < n; i++)
{
if(deg[i] == 0)
q.push(i);
}
while(!q.empty())
{
int cur = q.front();
q.pop();
deg[edges[cur]] -= 1;
if(deg[edges[cur]] == 0)
q.push(edges[cur]);
}
vector<int> ans(n);
function<void(int, int)> dfs = [&](int x, int depth)
{
ans[x] = depth;
for(auto y : g[x])
{
if(deg[y] == 0)
dfs(y, depth + 1);
}
};
//此时deg不为0的全是在基环上的点
for(int i = 0; i < n; i++)
{
if(deg[i] == 0)
continue;
vector<int> ring;
int cur = i;
while(deg[cur] > 0)
{
ring.push_back(cur);
deg[cur] = -1;
cur = edges[cur];
}
//此时ring当中是一个基环
//遍历基环中的每一个点
int len = ring.size();
for(int j = 0; j < len; j++)
dfs(ring[j], len);
}
return ans;
}
};