时间复杂度O(40n*n)的C++算法:修改图中的边权

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 时间复杂度O(40n*n)的C++算法:修改图中的边权

本篇源码下载

点击下载

1.12.1. 题目

给你一个 n 个节点的 无向带权连通 图,节点编号为 0 到 n - 1 ,再给你一个整数数组 edges ,其中 edges[i] = [ai, bi, wi] 表示节点 ai 和 bi 之间有一条边权为 wi 的边。

部分边的边权为 -1(wi = -1),其他边的边权都为 正 数(wi > 0)。

你需要将所有边权为 -1 的边都修改为范围 [1, 2 * 10^9] 中的 正整数 ,使得从节点 source 到节点 destination 的 最短距离 为整数 target 。如果有 多种 修改方案可以使 source 和 destination 之间的最短距离等于 target ,你可以返回任意一种方案。

如果存在使 source 到 destination 最短距离为 target 的方案,请你按任意顺序返回包含所有边的数组(包括未修改边权的边)。如果不存在这样的方案,请你返回一个 空数组 。

注意:你不能修改一开始边权为正数的边。

1 <= n <= 100

1 <= edges.length <= n * (n - 1) / 2

edges[i].length == 3

0 <= ai, bi < n

wi = -1 或者 1 <= wi <= 107

ai != bi

0 <= source, destination < n

source != destination

1 <= target <= 109

输入的图是连通图,且没有自环和重边。

分析

难点分析

任意边的权加1,则任意两点的最短路径要么不变,要么加1。前者对应至少有一条最短路径不经过此边;后者对应所有最短路径都经过此边。首先所有可变权的边,设置为1,每轮选择一条可以加权的权边加1。时间复杂度O(109*边数),时间复杂度太高,改成按顺序处理可变权边,就可以用二分法了,时间复杂度降为O(log(109*边数)约等于O(40)。

时间复杂度

每轮需要寻找最短路径,由于是稠密图,所以用朴素迪氏寻找单源路径。总时间复杂度是:O(log(10^9边数)nn),越O(40100*100)。

大致流程

如果可边权边设置为最大值,如果最短路径小于目标值,返回空。

如果可边权边设置为最小值,如果最短路径大于目标值,返回空。

二分寻找合适的值。

代码讲解

m_vMore0Edge,m_vLess0Edge分别记录不可变权边和可边权边。

CNeiBo3 是封装好的类,用与将权边转成邻接表。

CN2Dis 是封装好的求单源最短路径的类。

Do,求最短路径。

CalLess0Edge将增加的权分配给各边。

核心代码

//朴素迪杰斯特拉算法
class CN2Dis
{
public:
CN2Dis(int iSize) :m_iSize(iSize), DIS(m_vDis), PRE(m_vPre)
{
}
void Cal(int start, const vector<vector<pair<int, int>>>& vNeiB)
{
m_vDis.assign(m_iSize, -1);
m_vPre.assign(m_iSize, -1);
vector vDo(m_iSize);//点是否已处理
auto AddNode = [&](int iNode)
{
//const int iPreNode = m_vPre[iNode];
long long llPreDis = m_vDis[iNode];
vDo[iNode] = true;
  for (const auto& it : vNeiB[iNode])
  {
    if (vDo[it.first])
    {
      continue;
    }
    if ((-1 == m_vDis[it.first]) || (it.second + llPreDis < m_vDis[it.first]))
    {
      m_vDis[it.first] = it.second + llPreDis;
      m_vPre[it.first] = iNode;
    }
  }
  long long llMinDis = LLONG_MAX;
  int iMinIndex = -1;
  for (int i = 0; i < m_vDis.size(); i++)
  {
    if (vDo[i])
    {
      continue;
    }
    if (-1 == m_vDis[i])
    {
      continue;
    }
    if (m_vDis[i] < llMinDis)
    {
      iMinIndex = i;
      llMinDis = m_vDis[i];
    }
  }
  return (LLONG_MAX == llMinDis) ? -1 : iMinIndex;
};
int next = start;
m_vDis[start] = 0;
while (-1 != (next = AddNode(next)));
}
void Cal(int start, const vector<vector>& mat)
{
m_vDis.assign(m_iSize, LLONG_MAX);
m_vPre.assign(m_iSize, -1);
vector vDo(m_iSize);//点是否已处理
auto AddNode = [&](int iNode)
{
long long llPreDis = m_vDis[iNode];
vDo[iNode] = true;
for (int i = 0; i < m_iSize; i++)
{
if (vDo[i])
{
continue;
}
const long long llCurDis = mat[iNode][i];
if (llCurDis <= 0)
{
continue;
}
m_vDis[i] = min(m_vDis[i], m_vDis[iNode] + llCurDis);
}
long long llMinDis = LLONG_MAX;
int iMinIndex = -1;
for (int i = 0; i < m_iSize; i++)
{
if (vDo[i])
{
continue;
}
if (m_vDis[i] < llMinDis)
{
iMinIndex = i;
llMinDis = m_vDis[i];
}
}
if (LLONG_MAX == llMinDis)
{
return -1;
}
m_vPre[iMinIndex] = iNode;
  return iMinIndex;
};
int next = start;
m_vDis[start] = 0;
while (-1 != (next = AddNode(next)));
}
const vector& DIS;
const vector& PRE;
private:
const int m_iSize;
vector m_vDis;//各点到起点的最短距离
vector m_vPre;//最短路径的前一点
};
class CNeiBo3
{
public:
CNeiBo3(int n, vector<vector>& edges, bool bDirect, int iBase = 0)
{
m_vNeiB.resize(n);
AddEdges(edges, bDirect, iBase);
}
CNeiBo3(int n)
{
m_vNeiB.resize(n);
}
void AddEdges(vector<vector>& edges, bool bDirect, int iBase = 0)
{
for (const auto& v : edges)
{
m_vNeiB[v[0] - iBase].emplace_back(v[1] - iBase, v[2]);
if (!bDirect)
{
m_vNeiB[v[1] - iBase].emplace_back(v[0] - iBase, v[2]);
}
}
}
vector<vector<std::pair<int,int>>> m_vNeiB;
};
class Solution {
public:
  vector<vector<int>> modifiedGraphEdges(int n, vector<vector<int>>& edges, int source, int destination, int target) {
    m_n = n;
    m_src = source;
    m_dest = destination;
    for (const auto& v : edges)
    {
      if (-1 == v[2])
      {
        m_vLess0Edge.emplace_back(v);
      }
      else
      {
        m_vMore0Edge.emplace_back(v);
      }
    }
    long long left = 0, r = (long long)(1000 * 1000 * 1000-1)* m_vLess0Edge.size()+1;
    while (r - left > 1)
    {
      const auto mid = left + (r - left) / 2;
      const long long ll = Do(mid);
      if ( ll == target )
      {
        m_vLess0Edge.insert(m_vLess0Edge.end(), m_vMore0Edge.begin(), m_vMore0Edge.end());
        return m_vLess0Edge;
      }
      else if (ll > target)
      {
        r = mid;
      }
      else
      {
        left = mid;
      }
    }
    const long long ll = Do(left);
    if (target == ll)
    {
      m_vLess0Edge.insert(m_vLess0Edge.end(), m_vMore0Edge.begin(), m_vMore0Edge.end());
      return m_vLess0Edge;
    }
    return {};
  }
  long long Do(long long llAdd)
  {
    CalLess0Edge(llAdd);
    CNeiBo3 neiBo(m_n);
    neiBo.AddEdges(m_vMore0Edge,false);
    neiBo.AddEdges(m_vLess0Edge, false);
    CN2Dis dis(m_n);
    dis.Cal(m_src, neiBo.m_vNeiB);
    return dis.DIS[m_dest];
  }
  void CalLess0Edge(long long llAdd)
  {
    for (auto& v : m_vLess0Edge)
    {
      const int curAdd = (int)min(llAdd, (long long)1000 * 1000 * 1000 - 1);
      v[2] = 1 + curAdd;
      llAdd -= curAdd;
    }
  }
  int m_n;
  int m_src;
  int m_dest;
  vector<vector<int>> m_vMore0Edge,m_vLess0Edge;
};

测试用例

由于本文篇幅过长,请自行下载测试样例。


其它

视频课程

要是你认为本篇难道较大,不好入手,推荐你先学习基础算法的课程,我已完成部分,余下部分持续更新中,就在CSDN学院。

https://edu.csdn.net/course/detail/38771

C#入职培训、C++入职培训等课程

https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17

相关下载

如果你想观其大略,建设下载《闻缺陷则喜算法册》doc版

https://download.csdn.net/download/he_zhidan/88348653

相关文章
|
6月前
|
存储 监控 算法
基于 C++ 哈希表算法实现局域网监控电脑屏幕的数据加速机制研究
企业网络安全与办公管理需求日益复杂的学术语境下,局域网监控电脑屏幕作为保障信息安全、规范员工操作的重要手段,已然成为网络安全领域的关键研究对象。其作用类似网络空间中的 “电子眼”,实时捕获每台电脑屏幕上的操作动态。然而,面对海量监控数据,实现高效数据存储与快速检索,已成为提升监控系统性能的核心挑战。本文聚焦于 C++ 语言中的哈希表算法,深入探究其如何成为局域网监控电脑屏幕数据处理的 “加速引擎”,并通过详尽的代码示例,展现其强大功能与应用价值。
158 2
|
7月前
|
存储 算法 C++
Windows共享文件:探秘C++实现的B树索引算法奇境
在数字化时代,Windows共享文件的高效管理至关重要。B树算法以其自平衡多路搜索特性,在文件索引与存储优化中表现出色。本文探讨B树在Windows共享文件中的应用,通过C++实现具体代码,展示其构建文件索引、优化数据存储的能力,提升文件检索效率。B树通过减少磁盘I/O操作,确保查询高效,为企业和个人提供流畅的文件共享体验。
|
4月前
|
存储 监控 算法
基于跳表数据结构的企业局域网监控异常连接实时检测 C++ 算法研究
跳表(Skip List)是一种基于概率的数据结构,适用于企业局域网监控中海量连接记录的高效处理。其通过多层索引机制实现快速查找、插入和删除操作,时间复杂度为 $O(\log n)$,优于链表和平衡树。跳表在异常连接识别、黑名单管理和历史记录溯源等场景中表现出色,具备实现简单、支持范围查询等优势,是企业网络监控中动态数据管理的理想选择。
148 0
|
6月前
|
监控 算法 数据处理
基于 C++ 的 KD 树算法在监控局域网屏幕中的理论剖析与工程实践研究
本文探讨了KD树在局域网屏幕监控中的应用,通过C++实现其构建与查询功能,显著提升多维数据处理效率。KD树作为一种二叉空间划分结构,适用于屏幕图像特征匹配、异常画面检测及数据压缩传输优化等场景。相比传统方法,基于KD树的方案检索效率提升2-3个数量级,但高维数据退化和动态更新等问题仍需进一步研究。未来可通过融合其他数据结构、引入深度学习及开发增量式更新算法等方式优化性能。
181 17
|
5月前
|
存储 机器学习/深度学习 算法
基于 C++ 的局域网访问控制列表(ACL)实现及局域网限制上网软件算法研究
本文探讨局域网限制上网软件中访问控制列表(ACL)的应用,分析其通过规则匹配管理网络资源访问的核心机制。基于C++实现ACL算法原型,展示其灵活性与安全性。文中强调ACL在企业与教育场景下的重要作用,并提出性能优化及结合机器学习等未来研究方向。
151 4
|
5月前
|
机器学习/深度学习 存储 算法
基于 C++ 布隆过滤器算法的局域网上网行为控制:URL 访问过滤的高效实现研究
本文探讨了一种基于布隆过滤器的局域网上网行为控制方法,旨在解决传统黑白名单机制在处理海量URL数据时存储与查询效率低的问题。通过C++实现URL访问过滤功能,实验表明该方法可将内存占用降至传统方案的八分之一,查询速度提升约40%,假阳性率可控。研究为优化企业网络管理提供了新思路,并提出结合机器学习、改进哈希函数及分布式协同等未来优化方向。
160 0
|
7月前
|
存储 监控 算法
基于 C++ 哈希表算法的局域网如何监控电脑技术解析
当代数字化办公与生活环境中,局域网的广泛应用极大地提升了信息交互的效率与便捷性。然而,出于网络安全管理、资源合理分配以及合规性要求等多方面的考量,对局域网内计算机进行有效监控成为一项至关重要的任务。实现局域网内计算机监控,涉及多种数据结构与算法的运用。本文聚焦于 C++ 编程语言中的哈希表算法,深入探讨其在局域网计算机监控场景中的应用,并通过详尽的代码示例进行阐释。
161 4
|
8月前
|
存储 算法 安全
企业员工数据泄露防范策略:基于 C++ 语言的布隆过滤器算法剖析[如何防止员工泄密]
企业运营过程中,防范员工泄密是信息安全领域的核心议题。员工泄密可能致使企业核心数据、商业机密等关键资产的流失,进而给企业造成严重损失。为应对这一挑战,借助恰当的数据结构与算法成为强化信息防护的有效路径。本文专注于 C++ 语言中的布隆过滤器算法,深入探究其在防范员工泄密场景中的应用。
187 8
|
9月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
5月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
158 0

热门文章

最新文章