草庐IT

「Floyd」社交网络

孤星·璀璨 2023-03-28 原文

本题为3月13日23上半学期集训每日一题中B题的题解

可前往我的博客中阅读

题面

题目描述

在社交网络(social network)的研究中,我们常常使用图论概念去解释一些社会现象。不妨看这样的一个问题。在一个社交圈子里有n个人,人与人之间有不同程度的关系。我们将这个关系网络对应到一个n个结点的无向图上,两个不同的人若互相认识,则在他们对应的结点之间连接一条无向边,并附上一个正数权值c,c越小,表示两个人之间的关系越密切。

我们可以用对应结点之间的最短路长度来衡量两个人s和t之间的关系密切程度,注意到最短路径上的其他结点为s和t的联系提供了某种便利, 即这些结点对于s 和t之间的联系有一定的重要程度。我们可以通过统计经过一个结点v的最短路径的数目来衡量该结点在社交网络中的重要程度。

考虑到两个结点A和B之间可能会有多条最短路径。我们修改重要程度的定义如下:令 \(C_{s,t}\) 表示从s到t的不同的最短路的数目, \(C_{s,t}(v)\) 表示经过v从s到t的最短路的数目;则定义 \(I(v) = \sum_{s\neq v,t \neq v} \frac{C_{s,t}(v)}{C_{s,t}}\) 为结点v在社交网络中的重要程度。

为了使 \(I(v)\)\(C_{s,t}(v)\) 有意义,我们规定需要处理的社交网络都是连通的无向图,即任意两个结点之间都有一条有限长度的最短路径。

现在给出这样一幅描述社交网络s的加权无向图,请你求出每一个结点的重要程度。

输入

第一行有两个整数,n和m,表示社交网络中结点和无向边的数目。在无向图中,我们将所有结点从1到n进行编号。

接下来m行,每行用三个整数a, b, c描述一条连接结点a和b,权值为c的无向边。注意任意两个结点之间最多有一条无向边相连,无向图中也不会出现自环(即不存在一条无向边的两个端点是相同的结点)。

输出

包括n行,每行一个实数,精确到小数点后3位。第i行的实数表示结点i在社交网络中的重要程度。

样例输入

4 4
1 2 1
2 3 1
3 4 1
4 1 1

样例输出

1.000
1.000
1.000
1.000

提示

社交网络如下图所示。

对于1号结点而言,只有2号到4号结点和4号到2号结点的最短路经过1号结点,而2号结点和4号结点之间的最短路又有2条。因而根据定义,1号结点的重要程度计算为1/2+1/2=1。由于图的对称性,其他三个结点的重要程度也都是1。

50%的数据中: \(n\leq 10,m\leq 45\)

100%的数据中: \(n\leq 100,m\leq 4500\)

任意一条边的权值c是正整数,满足: \(1\leq c\leq 1000\) 所有数据中保证给出的无向图连通,且任意两个结点之间的最短路径数目不超过1010。


思路分析

想要求出最短路的条数,显然首先要求出最短路的长度(或者说是需要去求最短路的长度).此题为多源最短路问题,且节点数较小,所以可以尝试采用Floyd算法来解决.

如果你不知道什么是Floyd算法,请自行搜索学习,这里贴两篇个人找到的博客,这篇博客可以用来学习一下Floyd算法的基本知识,这篇博客可以用来学习更多的内容,如正确性证明等.也可自行搜索别的博客、视频、书籍等进行学习,这里不过多赘述.

由题意,我们需要维护出每两个点之间最短路的条数,以及这些最短路中经过某个点的条数.简单回忆Floyd算法求解最短路的过程可知:

  • 当一个中间点可以减少两个点之间的距离时,我们便用这个中间点来松弛这条边,此时,我们可以认为这个中间点加入了那两个点暂时的(或者说当前递推阶段的)最短路中(不理解这句话建议回去看看Floyd算法),我们可以由此,加上分步乘法计数原理,便计算出每个递推阶段中两个点之间最短路的数量;
  • 当我们用一个中间点尝试松弛一条边时,如果发现它松弛后的结果与这两个点当前阶段递推出来的最短距离是一致的,那么就说明这个点作为中间点也可以在当前阶段产生出同样长度的最短路.根据分类加法计数原理,我们可以以此来更新(或者说补充)当前阶段中两个点之间最短路的数量.

但是Floyd算法是一个动态的算法,如何在这个动态的过程中维护出最终的最短路数量呢?我们可以和Floyd算法一样,再开一个维度表示阶段(即当前正在用来松弛的点的编号),使用动态规划的方法进行维护.可得如下状态转移方程( \(dp[k][i][j]\) 表示当前用点k来松弛时,点i到j之间最短路的数量):

\(dp[k][i][j] = \begin{cases} 0, k = 0且(i,j)之间没有直接的边相连\\ 1, k = 0且(i,j)之间有直接的边相连(由于还未开始松弛,所以认为这条边就是最短路)\\ dp[k - 1][i][k] \times dp[k - 1][k][j], k \neq 0且当前k能松弛点对(i,j)之间的距离\\ dp[k - 1][i][j] + dp[k - 1][i][k] \times dp[k - 1][k][j], k \neq 0且当前k松弛后与原本点对(i,j)之间的最短距离一致\\ dp[k - 1][i][j], k \neq 0且当前k不能松弛点对(i,j)之间的距离 \end{cases}\)

显然,这个状态转移方程也可以使用和Floyd算法同样的方法来进行空间压缩,去掉这个表示阶段的维度,最终我们可以得到一个和Floyd几乎完全一致的形式.由于能进行空间压缩的原因以及方式和Floyd算法几乎完全一致,所以此处不多赘述,直接展示空间压缩后的状态转移规则:

  • 初始时,还未开始松弛,此时我们认为两个点之间的最短距离就是它们之间连着的边的距离,此时直接相连的点对 \((i,j)\) 之间有一条当前阶段的最短路, \(dp[i][j] = 1\) ,而之间没有边直接相连的点对 \((i,j)\) 之间没有当前阶段的最短路, \(dp[i][j] = 0\) .
  • 在Floyd递推的过程中,如果当前中间点k能够用来松弛点对 \((i,j)\) 之间的距离,那么在松弛的同时,我们将 $ dp[i][j] $ 重新计算为 $ dp[i][k] \times d[k][j] $ ,即当前阶段i到k的最短路数量和k到j的最短路数量之积(因为这里是两段路,视为两步,所以使用分步乘法计算原理).
  • 在Floyd递推的过程中,如果当前中间点k尝试松弛点对 \((i,j)\) 之间的距离时,发现他和当前点对之间的最短距离一致,证明通过这个点也能产生最短路,我们将 $ dp[i][j] $ 加上 $ dp[i][k] \times d[k][j] $ ,把这一段最短路的数量算上.

如此,我们只需要在Floyd的过程中,同步进行对dp数组的操作,即可维护出最终每一对点之间最短路径的数量.

然后我们再来看题目要求的这个式子,这是一个累加式,我们需要求出,每一个点对(s,t)之间的最短路数量,以及这些最短路中经过点v的最短路数量.其中,点对(s,t)之间的最短路数量我们已经求好了,而这些最短路中经过点v的最短路数量,显然可以依据分步乘法计数原理,通过 \(dp[s][v] * dp[v][t]\) 计算出.此外,额外规定,如果当前点不在最短路中,这个数是0.在这两个数量都计算出后,我们只需要按照给定的这个式子除一下,最后全部累加起来即可.

最后注意一下,最短路的数量可能会超过int类型的范围,所以这里我们需要用long long类型来保存.

参考代码

时间复杂度: \(O(N^3 + M)\) (计入输入使用时间)

空间复杂度: \(O(N^2)\)

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int n, m;
    cin >> n >> m;
    vector<vector<int>> g(n, vector<int>(n, 0x3f3f3f3f)); // Floyd最好直接用邻接矩阵
    vector<vector<i64>> dp(n, vector<i64>(n)); // 维护两条边之间最短路的数量,注意会爆int,要开成long long

    // 输入各边
    while (m--) {
        int a, b, c;
        cin >> a >> b >> c;
        a--; // 下标转为从0开始
        b--;
        g[a][b] = c;
        g[b][a] = c; // 无向边
        dp[a][b] = 1;
        dp[b][a] = 1; // 无向边
    }

    // Floyd
    for (int k = 0; k < n; k++) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (g[i][j] > g[i][k] + g[k][j]) { // 当前点可用来松弛
                    g[i][j] = g[i][k] + g[k][j]; // 松弛
                    dp[i][j] = dp[i][k] * dp[k][j]; // 利用乘法原理重新计算最短路数量
                } else if (g[i][j] == g[i][k] + g[k][j]) { // 当前点如果用来松弛,与原本效果一样,是另一条最短路
                    dp[i][j] += dp[i][k] * dp[k][j]; // 利用乘法原理更新最短路数量
                }
            }
        }
    }

    // 计算题目要求的那个公式的结果
    for (int v = 0; v < n; v++) { // 自变量
        double ans = 0;

        // 计算求和式
        for (int s = 0; s < n; s++) {
            for (int t = 0; t < n; t++) {
                if (s != t && s != v && t != v && g[s][t] == g[s][v] + g[v][t]) { // 当前点是最短路的一部分
                    ans += (double)(dp[s][v] * dp[v][t]) / dp[s][t];
                }
            }
        }
        cout << fixed << setprecision(3) << ans << "\n"; // 输出样例是三位小数,所以这里我保留了三位小数
    }

    return 0;
}

"正是我们每天反复做的事情,最终造就了我们,优秀不是一种行为,而是一种习惯" ---亚里士多德

有关「Floyd」社交网络的更多相关文章

  1. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  2. 网络编程套接字 - 2

    网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识

  3. ruby - 检查网络文件是否存在,而不下载它? - 2

    是否可以在不实际下载文件的情况下检查文件是否存在?我有这么大的(~40mb)文件,例如:http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm这与ruby​​不严格相关,但如果发件人可以设置内容长度就好了。RestClient.get"http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm",headers:{"Content-Length"=>100} 最佳答案

  4. ruby - 404 未找到,但可以从网络浏览器正常访问 - 2

    我在这方面尝试了很多URL,在我遇到这个特定的之前,它们似乎都很好:require'rubygems'require'nokogiri'require'open-uri'doc=Nokogiri::HTML(open("http://www.moxyst.com/fashion/men-clothing/underwear.html"))putsdoc这是结果:/Users/macbookair/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/open-uri.rb:353:in`open_http':404NotFound(OpenURI::HT

  5. 深度学习12. CNN经典网络 VGG16 - 2

    深度学习12.CNN经典网络VGG16一、简介1.VGG来源2.VGG分类3.不同模型的参数数量4.3x3卷积核的好处5.关于学习率调度6.批归一化二、VGG16层分析1.层划分2.参数展开过程图解3.参数传递示例4.VGG16各层参数数量三、代码分析1.VGG16模型定义2.训练3.测试一、简介1.VGG来源VGG(VisualGeometryGroup)是一个视觉几何组在2014年提出的深度卷积神经网络架构。VGG在2014年ImageNet图像分类竞赛亚军,定位竞赛冠军;VGG网络采用连续的小卷积核(3x3)和池化层构建深度神经网络,网络深度可以达到16层或19层,其中VGG16和VGG

  6. 【网络】-- 网络基础 - 2

    (本文是网络的宏观的概念铺垫)目录计算机网络背景网络发展认识"协议"网络协议初识协议分层OSI七层模型TCP/IP五层(或四层)模型报头以太网碰撞路由器IP地址和MAC地址IP地址与MAC地址总结IP地址MAC地址计算机网络背景网络发展        是最开始先有的计算机,计算机后来因为多项技术的水平升高,逐渐的计算机变的小型化、高效化。后来因为计算机其本身的计算能力比较的快速:独立模式:计算机之间相互独立。    如:有三个人,每个人做的不同的事物,但是是需要协作的完成。    而这三个人所做的事是需要进行协作的,然而刚开始因为每一台计算机之间都是互相独立的。所以前面的人处理完了就需要将数据

  7. 常见网络安全产品汇总(私信发送思维导图) - 2

    安全产品安全网关类防火墙Firewall防火墙防火墙主要用于边界安全防护的权限控制和安全域的划分。防火墙•信息安全的防护系统,依照特定的规则,允许或是限制传输的数据通过。防火墙是一个由软件和硬件设备组合而成,在内外网之间、专网与公网之间的界面上构成的保护屏障。下一代防火墙•下一代防火墙,NextGenerationFirewall,简称NGFirewall,是一款可以全面应对应用层威胁的高性能防火墙,提供网络层应用层一体化安全防护。生产厂家•联想网御、CheckPoint、深信服、网康、天融信、华为、H3C等防火墙部署部署于内、外网编辑额,用于权限访问控制和安全域划分。UTM统一威胁管理(Un

  8. 【Linux操作系统】——网络配置与SSH远程 - 2

    Linux操作系统——网络配置与SSH远程安装完VMware与系统后,需要进行网络配置。第一个目标为进行SSH连接,可以从本机到VMware进行文件传送,首先需要进行网络配置。1.下载远程软件首先需要先下载安装一款远程软件:FinalShell或者xhell7FinalShellxhell7FinalShell下载:Windows下载http://www.hostbuf.com/downloads/finalshell_install.exemacOS下载http://www.hostbuf.com/downloads/finalshell_install.pkg2.配置CentOS网络安装好

  9. ruby - 在 Ruby 中训练神经网络 - 2

    在神经网络方面,我完全是个初学者。我整天都在与ruby​​-fann和ai4r搏斗,不幸的是我没有任何东西可以展示,所以我想我会来到StackOverflow并询问这里的知识渊博的人。我有一组样本——每天都有一个数据点,但它们不符合我能够找出的任何明确模式(我尝试了几次回归)。不过,我认为看看是否有任何方法可以仅从日期预测future的数据会很好,而且我认为神经网络将是生成希望表达这种关系的函数的好方法.日期是DateTime对象,数据点是十进制数,例如7.68。我一直在将DateTime对象转换为float,然后除以10,000,000,000得到一个介于0和1之间的数字,我一直在将

  10. ruby - Heroku 和网络抓取 - 2

    我有一个nokigiri网络抓取工具,它发布到我试图发布到heroku的数据库。我有一个sinatra应用程序前端,我想从数据库中获取它。我是Heroku和Web开发的新手,不知道处理此类问题的最佳方法。我是否必须将上传到数据库的网络爬虫脚本放在sinatra路由下(如mywebsite.com/scraper),并让它变得如此模糊以至于没有人访问它?最后,我想让sinatra部分成为一个从数据库中提取的restapi。感谢大家的参与 最佳答案 您可以采用两种方法。第一个是通过控制台使用herokurunYOURCMD运行scrap

随机推荐