一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第28天,点击查看活动详情。
题目链接:2242. 节点序列的最大得分
题目描述
给你一个 n 个节点的 无向图 ,节点编号为 0 到 n - 1 。
给你一个下标从 0 开始的整数数组 scores ,其中 scores[i] 是第 i 个节点的分数。同时给你一个二维整数数组 edges ,其中 edges[i] = [ai, bi] ,表示节点 和 之间有一条 无向 边。
一个合法的节点序列如果满足以下条件,我们称它是 合法的 :
- 序列中每 相邻 节点之间有边相连。
- 序列中没有节点出现超过一次。
节点序列的分数定义为序列中节点分数之 和 。
请你返回一个长度为 4 的合法节点序列的最大分数。如果不存在这样的序列,请你返回 -1 。
提示:
n == scores.lengthedges[i].length == 2- 不会有重边。
示例 1:
输入:scores = [5,2,9,8,4], edges = [[0,1],[1,2],[2,3],[0,2],[1,3],[2,4]]
输出:24
解释:上图为输入的图,节点序列为 [0,1,2,3] 。
节点序列的分数为 5 + 2 + 9 + 8 = 24 。
观察可知,没有其他节点序列得分和超过 24 。
注意节点序列 [3,1,2,0] 和 [1,0,2,3] 也是合法的,且分数为 24 。
序列 [0,3,2,4] 不是合法的,因为没有边连接节点 0 和 3 。
示例 2:
输入:scores = [9,20,6,4,11,12], edges = [[0,3],[5,3],[2,4],[1,3]]
输出:-1
解释:上图为输入的图。
没有长度为 4 的合法序列,所以我们返回 -1 。
整理题意
题目给出一个 无向图 ,图中每个节点对应一个节点分数,scores[i] 表示节点 i 所对应的节点分数,要求在图中找到长度为 4 的节点链,每个节点不同且使得节点分数之和最大。返回最大节点分数和,如果不存在长度为 4 的节点链返回 -1。
解题思路分析
习惯性动作,首先观察题目数据范围:
- 点和边的数量都是在
5e4以内;(数量较大,暴力会TLE超时) - 节点分数在
1e8以内。(长度为4的节点链最大节点分数和为4e8,没有超过int数据范围)
由于点和边的数量很大,暴力搜索会超时,考虑枚举:
- 由于长度为
4,有4个节点、3条边,如果枚举点的话比较麻烦,这里考虑枚举边。 - 枚举中间的边相比于枚举端点两边的边效率更高,所以我们 枚举中间的边,贪心构造剩余的两条边即可 。
具体实现
由于我们的 核心思想 是 枚举中间的边,贪心构造剩余的两条边 ,既然是贪心,那么就要对边所连接的节点进行排序,按照节点分数进行排序:
- 选取与节点
x相连的分数最大节点a(除节点y,也就是a ≠ y) - 选取与节点
y相连的分数最大节点b(除节点x,也就是b ≠ x) - 同时需要保证节点
a ≠ b,如果a = b,我们需要贪心选择节点分数次大的。 - 当枚举的边能够构造成
a - x - y - b的序列时,我们记录节点分数和的最大值,每次取max即可。
复杂度分析
- 时间复杂度:,
n为节点数量,m为边的数量,对于n个节点的每条边所连接的节点进行排序需要 ,枚举每条边需要 。 - 空间复杂度:,
m为边的数量,仅需建图空间。
代码实现
class Solution {
public:
int maximumScore(vector<int>& scores, vector<vector<int>>& edges) {
int n = scores.size();
//建图 G[x][i] = y 表示 x和y有一条边
vector<vector<int>> G(n);
for(int i = 0; i < n; i++) G[i].clear();
int m = edges.size();
for(int i = 0; i < m; i++){
G[edges[i][0]].push_back(edges[i][1]);
G[edges[i][1]].push_back(edges[i][0]);
}
//对边按照scores从高到低排序
for(int i = 0; i < n; i++){
sort(G[i].begin(), G[i].end(), [&](int a, int b){
return scores[a] > scores[b];
});
}
int ans = -1;
//枚举中间边edges[i][0]和edges[i][1]之间的边
for(int i = 0; i < m; i++){
int x = edges[i][0];
int y = edges[i][1];
//长度为4的序列:a-x-y-b
vector<int> a, b;
a.clear();
b.clear();
//将备选的a和b放入数组
int Size = G[x].size();
for(int i = 0; i < Size && a.size() < 2; i++){
//不能与 y 点重复
if(G[x][i] != y) a.push_back(G[x][i]);
}
Size = G[y].size();
for(int i = 0; i < Size && b.size() < 2; i++){
//不能与 x 点重复
if(G[y][i] != x) b.push_back(G[y][i]);
}
//如果其中a或者b为空表示一段没有边,无法构成长度为4的序列
if(a.empty() || b.empty()) continue;
else{
int len = scores[x] + scores[y];
//如果两个首选(因为排过序,所以一定是最大的)
if(a[0] != b[0]) ans = max(ans, scores[a[0]] + scores[b[0]] + len);
else{
//如果 a[0] == b[0] 那么就看看次选
if(a.size() > 1) ans = max(ans, scores[a[1]] + scores[b[0]] + len);
if(b.size() > 1) ans = max(ans, scores[a[0]] + scores[b[1]] + len);
}
}
}
return ans;
}
};
总结
本题的核心思路是 枚举中间的边,贪心构造剩余的两条边 ,贪心构造的过程中需要选取备选节点,在枚举和构造的过程中注意判断重复节点即可。由于题目要求寻找的序列长度为 4,我们以这个作为突破点,巧妙选取枚举对象,贪心构造序列。
结束语
人生如棋,不能只看一时的得失,更要有长远的考量。凡事预则立,不预则废,成功没有什么太过复杂的思维方式,不过是凡事多想一步,遇事淡定处理。定好长期目标,走好当下的每一步,相信收获就在不远的前方。