【LeetCode 每日一题】1615. 最大网络秩(medium)

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+second1

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;
    }
};





菩萨蛮 【清·纳兰性德】

新寒中酒敲窗雨,残香细袅秋情绪。才道莫伤神,青衫湿一痕。
无聊成独卧,弹指韶光过。记得别伊时,桃花柳万丝。

  1. 中酒:非醉非醒的状态。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值