当前位置:首页 > 技术 > 正文内容

Floyd算法的高级应用

访客 技术 2026年7月12日 2

牛的旅行:求连通块连接后的最大直径

给定一组点和边,计算两个连通块连接后可能的最大直径。需要考虑两种情况:

  • 连接前的直径。
  • 连接后的直径。

以下是解决问题的步骤:

  1. 根据输入构建图。
  2. 使用Floyd算法计算所有点对之间的最短路径。
  3. 通过比较找到最大直径。

#include <iostream>
#include <cmath>
#include <cstring>

using namespace std;

const int MAX_N = 155;
const double INF = 1e20;

struct Point {
    double x, y;
};

int n;
Point points[MAX_N];
double dists[MAX_N][MAX_N], max_dist_per_point[MAX_N];

double calc_distance(Point a, Point b) {
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

void floyd() {
    for (int k = 0; k < n; ++k) {
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                if (dists[i][k] + dists[k][j] < dists[i][j]) {
                    dists[i][j] = dists[i][k] + dists[k][j];
                }
            }
        }
    }
}

int main() {
    cin >> n;
    for (int i = 0; i < n; ++i) {
        cin >> points[i].x >> points[i].y;
    }

    memset(dists, 0x3f, sizeof(dists));
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            if (i == j) dists[i][j] = 0;
            else dists[i][j] = calc_distance(points[i], points[j]);
        }
    }

    floyd();

    double max_diameter = 0;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            if (dists[i][j] < INF / 2) {
                max_dist_per_point[i] = max(max_dist_per_point[i], dists[i][j]);
            }
        }
        max_diameter = max(max_diameter, max_dist_per_point[i]);
    }

    cout << fixed << setprecision(6) << max_diameter << endl;
    return 0;
}

传递闭包与拓扑排序

利用Floyd算法求解传递闭包,并结合拓扑排序判断是否存在矛盾或唯一顺序。

  • 如果存在环,则无法确定顺序。
  • 如果拓扑序不唯一,则无法完全确定所有点的顺序。
  • 否则输出唯一的拓扑序列。

#include <iostream>
#include <cstring>

using namespace std;

const int MAX_N = 26;

bool graph[MAX_N][MAX_N];
bool visited[MAX_N];

void update_graph() {
    for (int k = 0; k < MAX_N; ++k) {
        for (int i = 0; i < MAX_N; ++i) {
            for (int j = 0; j < MAX_N; ++j) {
                graph[i][j] |= graph[i][k] && graph[k][j];
            }
        }
    }
}

char find_min_unvisited() {
    for (int i = 0; i < MAX_N; ++i) {
        if (!visited[i]) {
            bool is_min = true;
            for (int j = 0; j < MAX_N; ++j) {
                if (!visited[j] && graph[j][i]) {
                    is_min = false;
                    break;
                }
            }
            if (is_min) {
                visited[i] = true;
                return 'A' + i;
            }
        }
    }
    return '\0';
}

int main() {
    int n, m;
    while (cin >> n >> m && (n || m)) {
        memset(graph, 0, sizeof(graph));
        for (int i = 0; i < m; ++i) {
            char s[5];
            cin >> s;
            int a = s[0] - 'A', b = s[2] - 'A';
            graph[a][b] = true;
            update_graph();
        }

        // Check for cycles or ambiguity
        bool has_cycle = false, ambiguous = false;
        for (int i = 0; i < MAX_N; ++i) {
            if (graph[i][i]) {
                has_cycle = true;
                break;
            }
        }

        if (!has_cycle) {
            for (int i = 0; i < MAX_N; ++i) {
                for (int j = i + 1; j < MAX_N; ++j) {
                    if (!graph[i][j] && !graph[j][i]) {
                        ambiguous = true;
                        break;
                    }
                }
            }
        }

        if (has_cycle) {
            cout << "Inconsistency found." << endl;
        } else if (ambiguous) {
            cout << "Cannot determine sorted sequence." << endl;
        } else {
            memset(visited, 0, sizeof(visited));
            string result = "";
            for (int i = 0; i < n; ++i) {
                result += find_min_unvisited();
            }
            cout << "Sorted sequence: " << result << endl;
        }
    }
    return 0;
}

最小环问题

使用Floyd算法寻找图中的最小环。

关键思路是假设当前环中编号最大的节点为k,然后枚举与其相连的两个节点i和j,计算环的长度并更新最小值。


#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAX_N = 110, INF = 0x3f3f3f3f;

int n, m;
int distances[MAX_N][MAX_N], original_graph[MAX_N][MAX_N];
int path_nodes[MAX_N][MAX_N], min_cycle_path[MAX_N], cycle_count;

void reconstruct_path(int i, int j) {
    if (path_nodes[i][j] == 0) return;
    int k = path_nodes[i][j];
    reconstruct_path(i, k);
    min_cycle_path[cycle_count++] = k;
    reconstruct_path(k, j);
}

int main() {
    cin >> n >> m;
    memset(original_graph, 0x3f, sizeof(original_graph));
    for (int i = 1; i <= n; ++i) original_graph[i][i] = 0;

    for (int i = 0; i < m; ++i) {
        int u, v, w;
        cin >> u >> v >> w;
        original_graph[u][v] = original_graph[v][u] = min(original_graph[u][v], w);
    }

    memcpy(distances, original_graph, sizeof(original_graph));

    int min_cycle_length = INF;
    for (int k = 1; k <= n; ++k) {
        for (int i = 1; i < k; ++i) {
            for (int j = i + 1; j < k; ++j) {
                if ((long long)distances[i][j] + original_graph[i][k] + original_graph[k][j] < min_cycle_length) {
                    min_cycle_length = distances[i][j] + original_graph[i][k] + original_graph[k][j];
                    cycle_count = 0;
                    min_cycle_path[cycle_count++] = k;
                    min_cycle_path[cycle_count++] = i;
                    reconstruct_path(i, j);
                    min_cycle_path[cycle_count++] = j;
                }
            }
        }

        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
                if (distances[i][j] > distances[i][k] + distances[k][j]) {
                    distances[i][j] = distances[i][k] + distances[k][j];
                    path_nodes[i][j] = k;
                }
            }
        }
    }

    if (min_cycle_length == INF) {
        cout << "No solution." << endl;
    } else {
        for (int i = 0; i < cycle_count; ++i) {
            cout << min_cycle_path[i] << " ";
        }
        cout << endl;
    }

    return 0;
}

恰好经过K条边的最短路径

结合矩阵快速幂和Floyd思想解决此问题。

  • 离散化处理点编号。
  • 通过快速幂优化计算过程。

#include <iostream>
#include <vector>
#include <cstring>
#include <map>

using namespace std;

const int MAX_N = 1010;
int K, total_nodes, edges, start_node, end_node;
map<int, int> node_id;
int graph[MAX_N][MAX_N], result_graph[MAX_N][MAX_N];

void matrix_multiply(int res[][MAX_N], int a[][MAX_N], int b[][MAX_N]) {
    static int temp[MAX_N][MAX_N];
    memset(temp, 0x3f, sizeof(temp));

    for (int i = 1; i <= total_nodes; ++i) {
        for (int j = 1; j <= total_nodes; ++j) {
            for (int k = 1; k <= total_nodes; ++k) {
                temp[i][j] = min(temp[i][j], a[i][k] + b[k][j]);
            }
        }
    }

    memcpy(res, temp, sizeof(temp));
}

void fast_power() {
    while (K) {
        if (K & 1) matrix_multiply(result_graph, result_graph, graph);
        matrix_multiply(graph, graph, graph);
        K >>= 1;
    }
}

int main() {
    cin >> K >> edges >> start_node >> end_node;
    memset(graph, 0x3f, sizeof(graph));

    node_id[start_node] = ++total_nodes;
    node_id[end_node] = ++total_nodes;
    start_node = node_id[start_node];
    end_node = node_id[end_node];

    for (int i = 0; i < edges; ++i) {
        int weight, u, v;
        cin >> weight >> u >> v;
        if (!node_id.count(u)) node_id[u] = ++total_nodes;
        if (!node_id.count(v)) node_id[v] = ++total_nodes;
        u = node_id[u];
        v = node_id[v];
        graph[u][v] = graph[v][u] = min(graph[u][v], weight);
    }

    memset(result_graph, 0x3f, sizeof(result_graph));
    for (int i = 1; i <= total_nodes; ++i) result_graph[i][i] = 0;

    fast_power();
    cout << result_graph[start_node][end_node] << endl;

    return 0;
}

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。