图论:1615. 最大网络秩(贪心,非完全图一定存在两个点之间没有边)

news2025/2/23 14:31:49

文章目录

  • 1.计算出度+排序+哈希
  • 2.枚举
  • 3.贪心
  • 4.思考

1615. 最大网络秩
在这里插入图片描述
在不考虑两座道路直接相连时,我们求出入度(或出度)最大的两个点即可。
若相连,则存在一条边,所以我们将边存入一个集合中,快速查找是否存在。

1.计算出度+排序+哈希

在使用哈希存边时,unordered_set不能存储pair<type1,type2>类型,set可以,但是set速度更慢。为了使用unordered_set,我们可以观察数据大小,注意到n≤100,因此我们可以将两个数合并成一个数:

  • 对于一条边(a,b),我们确保a<b(这样不会重复存边),将a保存在高2位,b保存在低2位。如(15,2),处理过后就是1502。这是一种映射关系,并且是无序边集到整数的一一映射。

排序: O ( n l o g n ) O(nlogn) O(nlogn)
求最大网络秩的最坏时间复杂度: O ( n 2 ) O(n^2) O(n2),因为要考虑两个顶点相连的情况。

class Solution {
public:
    int maximalNetworkRank(int n, vector<vector<int>>& roads) {
        vector< pair<int, int> > outDegrees(n);
        for(int i = 0; i < n; ++ i){
            outDegrees[i].second = i;
            outDegrees[i].first = 0;
        }
        unordered_set<int> st;
        for(auto & i : roads){
            outDegrees[i[0]].first ++;
            outDegrees[i[1]].first ++;
            int x = i[0], y = i[1];
            if(x > y) swap(x, y);
            st.insert(x * 100 + y);
        }

        sort(outDegrees.begin(),outDegrees.end());

        int mx = -1;

        for(int i = n - 1; i >= 1; -- i){
            int j = i - 1;
            for(; j >= 0; -- j){
                int tmp = outDegrees[i].first + outDegrees[j].first;
                int x = outDegrees[i].second;
                int y = outDegrees[j].second;
                if(x > y) swap(x, y);
                if(st.count(x * 100 + y)){
                    tmp -= 1;
                }
                if(tmp >= mx) mx = tmp;//只有严格小于才能跳过,等于时,后者可能更好,如4,4,4,前两个四有边,但第一个和第三个之间无边。
                else break;
            }
            if(j == i - 1) break;
        }

        return mx;
    }
};
  • 虽然会比枚举快,但最坏也是 n 2 n^2 n2了,直接枚举

2.枚举

时间复杂度: O ( n 2 ) O(n^2) O(n2)

class Solution {
public:
    int maximalNetworkRank(int n, vector<vector<int>>& roads) {
        vector<int> outDegrees(n, 0);
        unordered_set<int> st;

        for (const auto& road : roads) {
            outDegrees[road[0]]++;
            outDegrees[road[1]]++;
            int x = min(road[0], road[1]);
            int y = max(road[0], road[1]);
            st.insert(x * 100 + y);
        }

        int maxRank = 0;

        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                int rank = outDegrees[i] + outDegrees[j];
                if (st.count(i * 100 + j)) {
                    rank--;
                }
                maxRank = max(maxRank, rank);
            }
        }

        return maxRank;
    }
};

官方:
使用领接矩阵存储是否存在边,而没使用哈希。

class Solution {
public:
    int maximalNetworkRank(int n, vector<vector<int>>& roads) {
        vector<vector<bool>> connect(n, vector<bool>(n, false));
        vector<int> degree(n, 0);
        for (auto v : roads) {
            connect[v[0]][v[1]] = true;
            connect[v[1]][v[0]] = true;
            degree[v[0]]++;
            degree[v[1]]++;
        }

        int maxRank = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                int rank = degree[i] + degree[j] - (connect[i][j] ? 1 : 0);
                maxRank = max(maxRank, rank);
            }
        }
        return maxRank;
    }
};

3.贪心

我们考虑到方法一,我们使用了排序这个思想,使得从最大的选取,但是我们在之后发现了,即使是排序了我们也不得不可能使得内层循环是 O ( n ) O(n) O(n)次。

我们是否能有更好的办法呢?我们可以观察到,我们取得的两个最大值,最小的时候它们的秩也是它们之和再-1。那么我们还有必要考虑比这两个最大值其中的一个更小的值吗?没有了!因为更小的时候最大也是其中一个-1,这和只考虑他俩是没有区别的

有了这样一个想法,我们可以更快速的解决这个问题:

  1. 当出度的最大值有多个时,答案必然在这个最大值中选择两个产生,不管是否它们有连接。
  2. 当出度的最大值只有一个时,答案必然包含该最大值。

由于任何多次遍历都是 O ( n ) O(n) O(n),因此我们可以直接找到该最大值的集合。

class Solution {
public:
    int maximalNetworkRank(int n, vector<vector<int>>& roads) {
        vector< int > outDegrees(n);

        unordered_set<int> st;
        for(auto & i : roads){
            outDegrees[i[0]] ++;
            outDegrees[i[1]] ++;

            int x = i[0], y = i[1];
            if(x > y) swap(x, y);
            st.insert(x * 100 + y);
        }

        int mx = -1;//度的最大值
        for(auto & i : outDegrees){
            if(i > mx) mx = i;
        }

        vector<int> mxDegrees;//存储最大度的节点数

        for(int i = 0; i < n; ++ i){
            if(outDegrees[i] == mx) mxDegrees.emplace_back(i);
        }

        int ans = -1;
        if(mxDegrees.size() == 1){//最大度就一个
            for(int i = 0; i < n; ++ i){
                if(outDegrees[i] != mx){
                    int x = mxDegrees.back();
                    int y = i;
                    if(x > y) swap(x, y);
                    ans = max(outDegrees[i] + mx - (st.count(x * 100 + y) == 0 ? 0 : 1), ans);
                }
            }
        }else{
        	int m = roads.size();
            if (mxDegrees.size() * (mxDegrees.size() - 1) / 2 > m) {
                return mx * 2;
            }

            for(int i = 0; i < mxDegrees.size(); ++ i){
                for(int j = i + 1; j < mxDegrees.size(); ++ j){
                    ans = max(ans, mx * 2 - (st.count(mxDegrees[i] * 100 + mxDegrees[j]) == 0 ? 0 : 1));
                }
            }
        }

        return ans;
    }
};

在这里插入图片描述

4.思考

在这里插入图片描述
这里将所有出度为最大值的顶点放入到 f i r s t A r r firstArr firstArr集合中,然后考虑其定点数 x x x,与所有边的个数的关系。

实际上这里就是在考虑这样一个问题:
如果一个顶点边集构成的图不是一个完全图,那么是否一定存在两个点之间没有边?
答案是对的!

证明一下:
如果一个图是一个完全图,则必然这个图中的所有顶点之间存在两个点没有边相连。
如果我们将这个图的某条边删除,则这个图一定不是完全图,则删除的这条边的两个顶点没有边相连。
则我们可以知道如果一个图不是完全图,则必然可以通过它的完全图删除某些边得到,而每次删除的边都会使得删除的这条边的两个顶点没有边相连。
即一个图如果不是完全图,则必然存在两个点没有边相连。

所以题解中用到的优化就是,判断该图是否可能是完全图,如果是则没办法,如果不可能是则直接判定一定存在两个顶点没有边相连。

使用:

x ∗ ( x − 1 ) 2 与边数 m 比较 \frac{x*(x-1)}{2} 与边数m比较 2x(x1)与边数m比较
x ∗ ( x − 1 ) 2 \frac{x*(x-1)}{2} 2x(x1)代表了图是一个完全图是边的个数,如果这个个数比边数m大,则必然这个图不是完全图,即一定存在两个点之间没有边,既然这个集合里面的出度都一样,那么我们只需选取这两个没有边的点就可以得到答案为 2 ∗ m x 2*mx 2mx

我们注意到m是整个图中的边数,而不是 f i r s t A r r firstArr firstArr中的边数,原因在于求 f i r s t A r r firstArr firstArr中的边数比较复杂,直接使用m代替了。

当然这里思考并不是对这个题目而言的,这题目可能最坏还是 O ( n 2 ) O(n^2) O(n2),只需要思考出贪心,并理解为什么非完全图一定存在两个点之间没有边即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1954479.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[每周一更]-(第107期):经典面试题-从输入URL到页面加载发生了什么

文章目录 过程概述简化版&#xff1a;详细版&#xff1a;1. 用户输入URL2. 浏览器解析URL3. DNS解析4. TCP连接5. SSL/TLS握手&#xff08;如果使用HTTPS&#xff09;6. HTTP请求和响应7. 浏览器渲染页面8. 处理后续请求 一般前后端都可以考察问题&#xff0c;让参与者了解网页…

WordPress设置固定连接后提示404

WordPress设置固定链接后出现404错误通常是因为服务器的伪静态规则没有正确设置。以下是几种常见的服务器环境下的解决方案&#xff1a; 宝塔面板&#xff1a;如果服务器安装了宝塔面板&#xff0c;可以在宝塔面板中选择对应的WordPress伪静态规则并保存设置 。 Apache服务器&a…

星间链路的卫星节点网络接口IP地址规划问题 based on 卫星互联网Walker星座

★★★第p个轨道面上的第n个卫星节点[ XL_p_n ]的IPv4子网和网络接口地址规划★★★ IPv4子网问题&#xff1a;中间2个点分十进制分别表示[P:轨道面索引]和[N:当前轨道面上的卫星索引]。考虑Exata设置IPv4子网默认为 190.0.0.0 &#xff0c;不妨&#xff1a; 将某个轨道高度的W…

【通信模块】简单玩转WiFi模块(ESP32、ESP8266)

笔者学习太极创客的学习笔记&#xff0c;链接如下&#xff1a;www.taichimaker.com 前期准备 电脑端口 固件烧录 WIFI到网页 对应七层网络协议 WIFI工作模式&#xff08;链路层&#xff09; 接入点模式、无线中断模式、混合模式 IP协议&#xff08;网络层&#xff09; 子网…

【python】Python考研分数 线性回归模型预测(源码+论文)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

前端缓存问题(浏览器缓存和http缓存)- 解决办法

问题描述&#xff1a;前端代码更新&#xff0c;但因浏览器缓存问题&#xff0c;导致页面源代码并未更新 查看页面源代码的方法&#xff1a;鼠标右键&#xff0c;点击查看页面源代码 如图&#xff1a; 解决方法&#xff1a; 注&#xff1a;每执行一步&#xff0c;就检查一下浏览…

c生万物系列(加减乘除模篇)

为了提高c语言的运行效率&#xff0c;我们需要采用更高效的运算&#xff0c;那么切入点就是随处可见的基本运算符合&#xff0c;从底层架构考虑&#xff0c;加减乘除的效率比位运算低很多&#xff0c;为了能够更好迎合CPU的二进制&#xff0c;有必要取代基本的加减乘除以及求余…

Java----队列(Queue)

目录 1.队列&#xff08;Queue&#xff09; 1.1概念 1.2队列的使用 1.3队列的模拟实现 1.4循环队列 1.4.1循环队列下标偏移 1.4.2如何区分队列是空还是满 1.5双端队列 (Deque) 1.队列&#xff08;Queue&#xff09; 1.1概念 队列&#xff1a;只允许在一端进行插入数据…

GraphRAG:全局搜索方法在RAG系统中的应用

GraphRAG&#xff1a;全局搜索方法在RAG系统中的应用 传统RAG系统的局限性全局搜索方法的优势全局搜索的工作流程关键配置参数实现考虑结论 在检索增强生成&#xff08;Retrieval-Augmented Generation&#xff0c;RAG&#xff09;系统中&#xff0c;传统的基线方法在处理需要跨…

怀旧必玩!重返童年,扫雷游戏再度登场!

Python提供了一个标准的GUI&#xff08;图形用户界面&#xff09;工具包&#xff1a;Tkinter。它可以用来创建各种窗口、按钮、标签、文本框等图形界面组件。 而且Tkinter 是 Python 自带的库&#xff0c;无需额外安装。 Now&#xff0c;让我们一起来回味一下扫雷小游戏吧 扫…

数据结构与算法-13高级数据结构_树论(BtreeB+Tree)

Btree&BTree 1 btree-定义 B-树是一种自平衡的树形数据结构&#xff0c;它能够保持数据的有序性&#xff0c;并允许搜索、顺序访问、插入和删除操作都在对数时间内完成。与二叉树不同&#xff0c;B-树的每个节点可以拥有多于两个的子节点&#xff0c;这取决于树的阶&#…

* 快速排序的深入优化探讨

在算法设计领域&#xff0c;快速排序因其卓越的平均性能与广泛的应用场景而备受推崇。自1960年Tony Hoare提出以来&#xff0c;它已成为许多编程语言标准库中的核心排序方法。然而&#xff0c;随着数据规模的不断扩大和计算需求的日益复杂化&#xff0c;对快速排序进行更深入的…

docker环境下的verdaccio设置权限并配置域名.md

权限配置 一个管理员叫admin,可以读也可以发布一个普通用户叫qiuye,只可以读,不可以发布添加账号就自行创建添加即可,只需要更改config文件的配置项即可 packages:*/*: access: admin qiuyepublish: admin unpublish: admin **:access: admin qiuyepublish: admin unpublish…

数据结构系列-插入排序和希尔排序

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 排序的概念 常见的排序算法&#xff1a; 插入排序 直接插入排序是一种简单的插入排序法&#xff0c;其基本思想是&#xff1a; 把待排序的记录按其关键码值的大小逐个插入到…

如何合规与安全地利用专业爬虫工具,构建企业数据竞争优势

摘要&#xff1a; 本文深入探讨了在当今大数据时代&#xff0c;企业如何通过合规且安全的方式运用专业爬虫工具&#xff0c;有效收集并分析海量信息&#xff0c;进而转化为企业独有的数据优势。我们不仅会介绍最佳实践&#xff0c;还会讨论关键技术和策略&#xff0c;帮助企业…

virtuoso tran仿真中如何画出temperature的瞬态曲线

virtuoso tran仿真中如何画出温度的瞬态曲线&#xff1f; 在tran仿真中如果加入了瞬态热效应&#xff0c;设置Dynamic parameter&#xff0c;即时间随温度变化&#xff0c;如何plot temp vs. time曲线&#xff1f; 1.电路中加一根线和变量名相同 2.ADE L/XL Test Editor->Ou…

如何在安卓设备上运行Linux(使用termux+图形界面)加上换源等优化

我学生嘛&#xff0c;喜欢讲故事&#xff0c;你看看我大部分文章开头&#xff0c;都会有"事情的起因"一类话 当然这次也不例外哦 我最新获得了一个新平板&#xff0c;华为的matepad air&#xff0c;很喜欢。想捣鼓&#xff0c;不太懂&#xff0c;但好像鸿蒙不能直接…

MyBatis中的赋值语句:#{}和${}的区别差异(常见面试题)

我们开始先总结他们的差异&#xff0c;后面再使用代码展示差异 1.0.#{}和${}的差异 &#xff08;1&#xff09;${}可能存在sql注入的安全问题 &#xff08;2&#xff09;${}是即时sql&#xff08;参数直接拼接&#xff09;&#xff0c;不能进行缓存&#xff1b;#{}是预编译sq…

基于单文档的MFC图像增强

目录 function.h ColorEnhanceDib.h ColorEnhanceDib.cpp Dib.h Dib.cpp FrequencyFilterDib.h FrequencyFilterDib.cpp GrayTransformDib.h GrayTransformDib.cpp HistogramDib.h HistogramDib.cpp SharpenProcessDib.h SharpenProcessDib.cpp SmoothProcessDib.h Sm…

【Spring Boot】自动配置源码解析

目录 Spring-Boot-Starter一、准备配置类和 Bean 对象二、自动配置条件依赖三、Bean 的参数获取3.1 EnableConfigurationProperties 注解3.2 ConfigurationProperties 注解 四. Bean 的发现4.1 自己项目的 Bean 扫描4.2 jar 包的 Bean 扫描 五. Bean 的加载 自动配置总结 Sprin…