1615. 最大网络秩
给你一个节点个数和一个双向边集,要求返回任意两个点的度的和的最大值,如果这两个点直接相连,那之间的边只算一次,总数就要-1。
我们直接遍历双向边集,统计每个节点的度,同时用哈希表记录节点之间的连接情况。然后用 n 2 n^2 n2 时间去遍历所有节点的组合,寻找节点度之和的最大值,如果这两个节点之间有边,就在和的基础上-1。
模拟ac
class Solution {
public:
int maximalNetworkRank(int n, vector<vector<int>>& roads) {
vector<int> ans(n,0);
vector<vector<int>> vis(n,vector<int> (n,0));
int len=roads.size();
int res=0;
for(auto road:roads){
ans[road[0]]++;
ans[road[1]]++;
vis[road[0]][road[1]]++;
vis[road[1]][road[0]]++;
}
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
res=max(res,ans[i]+ans[j]-(vis[i][j]||vis[j][i]));
}
}
return res;
}
};
尝试优化
毫无疑问, n 2 n^2 n2 时间肯定不是最优的,只超越了29%。
然后尝试在遍历边集的时候维护一个最大值 f i r s t first first 和一个次大值 s e c o n d second second,最后判断一下二者之间是不是直连。若,则答案为 f i r s t + s e c o n d first+second first+second,否则为 f i r s t + s e c o n d − 1 first+second-1 first+second−1。
class Solution {
public:
int maximalNetworkRank(int n, vector<vector<int>>& roads) {
vector<int> ans(n,0);
int len=roads.size();
int res=0;
int first_num=0,firts_city;
int second_num=0,second_city;
for(int i=0;i<len;i++){
int x=roads[i][0];
int y=roads[i][1];
ans[x]++;
if(ans[x]>=first_num){
//更新最大值
second_num=first_num;
second_city=firts_city;
first_num=ans[x];
firts_city=x;
}
else if(ans[x]>=second_num){
//更新次大值
second_num=ans[x];
second_city=x;
}
ans[y]++;
if(ans[y]>=first_num){
second_num=first_num;
second_city=firts_city;
first_num=ans[y];
firts_city=y;
}
else if(ans[y]>=second_num){
second_num=ans[y];
second_city=y;
}
}
res=first_num+second_num;
// cout<<firts_city<<" "<<second_city<<endl;
for(int i=0;i<len;i++){
int x=roads[i][0];
int y=roads[i][1];
if((x==firts_city&&y==second_city)||(x==second_city&&y==firts_city)){
// cout<<"flag";
res-=1;
break;
}
}
for(int i=0;i<n;i++){
cout<<i<<" "<<ans[i]<<endl;
}
return res;
}
};
但是我们没法在更新的同时去考虑这两个点之间是否有重复边,所以可能得到的结果会比实际值小1。如下图所示,0和3、0和4,这两个节点组合的度之和都是4,然而由于在边集中[0,4]位置靠后,所以0和3的组合会被更新成0和4,然而0和4并不是最优组合,他俩之间有直连的边,而0和3之间没有,所以最后的结果比真实值小1。
优化ac
既然结果偏差是因为遗漏组合导致的,那我们把度数等于 f i r s t first first 和 s e c o n d second second 的点拿出来组合一下不就好了嘛。具体怎么做呢,前半部分和上面一样,得到最大值和次大值之后,我们遍历一遍统计度数的数组,每次拿这个最大值和次大值去和当前点计算一下度数和(包含直连判断),就得到答案了!三言两语说不太清楚,具体看代码注释。只需要 O ( m + n ) O(m+n) O(m+n)时间。
数组全都开大一个是为了给 f i r s t _ c i t y first\_city first_city 和 s e c o n d _ c i t y second\_city second_city 赋初值以解决边集为空的情况。如果边集为空,那 f i r s t _ c i t y first\_city first_city 和 s e c o n d _ c i t y second\_city second_city 就需要等于1~n-1之外的值,而这个下标需要进一步访问数组所以数组全都开大一格, f i r s t _ c i t y first\_city first_city 和 s e c o n d _ c i t y second\_city second_city 初值均为n。
class Solution {
public:
int maximalNetworkRank(int n, vector<vector<int>>& roads) {
vector<int> ans(n+1,0);//记录点的度数
vector<vector<int>> vis(n+1,vector<int> (n+1,0));//哈希表,记录两个点之间是否直连
int len=roads.size();
int res=0;
int first_num=0,firts_city=n;
int second_num=0,second_city=n;
for(int i=0;i<len;i++){
int x=roads[i][0];
int y=roads[i][1];
vis[x][x]++;
vis[y][x]++;
ans[x]++;
if(ans[x]>=first_num){
//更新最大值和最大度数的节点编号
second_num=first_num;
second_city=firts_city;
first_num=ans[x];
firts_city=x;
}
else if(ans[x]>=second_num){
//更新次大值
second_num=ans[x];
second_city=x;
}
ans[y]++;
if(ans[y]>=first_num){
second_num=first_num;
second_city=firts_city;
first_num=ans[y];
firts_city=y;
}
else if(ans[y]>=second_num){
second_num=ans[y];
second_city=y;
}
}
for(int i=0;i<n;i++){
if(i!=firts_city){
//必须判重,不能两个节点和自己组合
//用布尔运算一次性判断是否直连和减去重合边
res=max(res,first_num+ans[i]-(vis[firts_city][i]||vis[i][firts_city]));
}
if(i!=second_city){
res=max(res,second_num+ans[i]-(vis[second_city][i]||vis[i][second_city]));
}
}
return res;
}
};
菩萨蛮 【清·纳兰性德】
新寒中酒敲窗雨,残香细袅秋情绪。才道莫伤神,青衫湿一痕。 无聊成独卧,弹指韶光过。记得别伊时,桃花柳万丝。
- 中酒:非醉非醒的状态。