当前位置: 首页 > article >正文

图论——floyd算法

acwing1125.牛的旅行

1.先做一边 f l o y d floyd floyd,求出每个点到其他各点的最短距离,得到 d i s t [ ] [ ] dist[][] dist[][]数组。
2.求出 m a x d [ ] maxd[] maxd[]数组,存放每个点到可达点的距离最大值(遍历dist数组可得),遍历 m a x d maxd maxd可得到各个牧场内的最大的直径 r e s 1 res1 res1
3.连接两个不在同一牧场的点 ( i , j ) (i,j) (i,j),求出新牧场经过路径 d [ i ] [ j ] d[i][j] d[i][j]的所有可能直径中的最小值 r e s 2 = m i n ( d [ i ] [ j ] + m a x d [ i ] + m a x d [ j ] ) res2=min(d[i][j]+maxd[i]+maxd[j]) res2=min(d[i][j]+maxd[i]+maxd[j])
4.最后的答案就是 r e s 1 res1 res1 r e s 2 res2 res2的最大值。

#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
typedef pair<int,int> PII;
#define x first
#define y second
const int N = 155;
const int INF = 0x3f3f3f3f;
char g[N][N];
double maxd[N];
PII q[N];
double d[N][N];
int n;
double get_dist(int i, int j)
{
    double dx = q[i].x - q[j].x, dy = q[i].y - q[j].y;
    return sqrt(dx * dx + dy * dy);
}
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ )
        cin >> q[i].x >> q[i].y;
    for (int i = 1; i <= n; i ++ )
        cin >> g[i];
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
        {
            if (i != j)
            {
                if (g[i][j - 1] == '0') d[i][j] = INF;
                else d[i][j] = get_dist(i, j);
            }
        }

    for (int k = 1; k <= n; k ++)
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
                
    double res1 = 0;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            if (d[i][j] < INF) 
            {
                maxd[i] = max(maxd[i], d[i][j]);
                res1 = max(res1, d[i][j]);
            }
    double res2 = INF;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
        {
            if (d[i][j] >= INF)
            res2 = min(res2, get_dist(i, j) + maxd[i] + maxd[j]);
        }
    printf("%.6lf", max(res1, res2));
    return 0;
}

acwing343.排序

给定若干个元素和若干对二元关系且关系具有传递性“通过传递性推导出尽量多的元素之间的
关系”的问题便被称为传递闭包。
本题的主要思想就是考虑枚举到每一组关系时,我们能不能推出所有点对之间的关系,或者说会不会出现矛盾的情况。
我们定义 d [ i ] [ j ] d[i][j] d[i][j]表示 i , j i,j i,j两个点之间时候存在小于关系,使用floyd推出点对之间的关系, d [ i ] [ j ] ∣ = d [ i ] [ k ] & d [ k ] [ j ] d[i][j] |= d[i][k] \& d[k][j] d[i][j]=d[i][k]&d[k][j]

#include <iostream>
#include <cstring>
using namespace std;
const int N = 26;
int g[N][N];
int d[N][N];
bool st[N];
int n, m;
void floyd()
{
    memcpy(d, g, sizeof d);
    for (int k = 0; k < n; k ++ )
        for (int i = 0; i < n; i ++ )
            for (int j = 0; j < n; j ++ )
                d[i][j] |= d[i][k] & d[k][j];
    
       
}
int check()
{
    for (int i = 0; i < n; i ++ )
        if (d[i][i]) return 2;
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < i; j ++ )
            if (!d[i][j] && !d[j][i]) return 0;
    return 1;
}
char get_min()
{
    for (int i = 0; i < n; i ++ )
    {
        if (st[i]) continue;
        bool flag = true;
        for (int j = 0; j < n; j ++ )
        {
            if (!st[j] && d[j][i])
            {
                flag = false;
                break;
            }
        }
        if (flag)
        {
            st[i] = true;
            return i + 'A';
        }
    }
}
int main()
{
    while (cin >> n >> m, n || m)
    {
        memset(g, 0, sizeof g);
        int type = 0, t;
        for (int i = 1; i <= m; i ++ )
        {
            char str[5];
            cin >> str;
            int a = str[0] - 'A', b = str[2] - 'A';
            if (!type)
            {
                g[a][b] = 1;
                floyd();
                type = check();
                if (type) t = i;
            }
        }
        if (!type) puts("Sorted sequence cannot be determined.");
        else if (type == 2) printf("Inconsistency found after %d relations.\n", t);
        else{
            memset(st, 0, sizeof st);
            printf("Sorted sequence determined after %d relations: ", t);
            for (int i = 0; i < n; i ++ ) printf("%c", get_min());
            printf(".\n");
        }
    }
    return 0;
}

acwing344.观光之旅

我们考虑依次求出最大值为k且包含k的环的长度。
对于一个环 i − k − j − . . . − i i-k-j-...-i ikj...i来说,环的长度为 d i s t [ i ] [ j ] + w [ i ] [ k ] + w [ k ] [ j ] dist[i][j] +w[i][k]+w[k][j] dist[i][j]+w[i][k]+w[k][j],其中 d i s t [ i ] [ j ] dist[i][j] dist[i][j]为i到j的最短路, w [ i ] [ k ] + w [ k ] [ j ] w[i][k]+w[k][j] w[i][k]+w[k][j]为两条边权之和。我们对floyd算法进行分析:

 for (int k = 1; k <= n; k ++)
 {
 	//每次我们循环到这个位置时,已经求得了经过点不大于
 	//k-1的两点间的最短路径,正好对应了上面的dist[i][j],
 	//也正因此我们可以在此处来求最大值为k且包含k的环的长度
  	for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
 }
       
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 110, M = 10010;
int g[N][N];
int d[N][N];
int pos[N][N];
int path[N];
int n, m;
int cnt;
void get_path(int i, int j)
{
    int u = pos[i][j];
    if (u == 0) return ;
    get_path(i, u);
    path[cnt ++ ] = u;
    get_path(u, j);
    return ;
}
int main()
{
    cin >> n >> m;
    memset(g, 0x3f, sizeof g);
    for (int i = 1; i <= n; i ++ ) g[i][i] = 0;
    while (m -- )
    {
        int a, b, c;
        cin >> a >> b >> c;
        g[a][b] =  g[b][a] = min(g[a][b], c);
    }
    memcpy(d, g, sizeof d);
    int res = 0x3f3f3f3f;                                                                                                                                                       
    for (int k = 1; k <= n; k ++ )
    {
        for (int i = 1; i < k; i ++ )
        {
            for (int j = i + 1; j < k; j ++ )
            {
                if (res > (ll)d[i][j] + g[i][k] + g[k][j])
                {
                    res = d[i][j] + g[i][k] + g[k][j];
                    cnt = 1;
                    get_path(i, j);
                    path[cnt ++ ] = j, path[cnt ++ ] = k, path[cnt ++ ] = i;
                }
            }
        }
        for (int i = 1; i <= n; i ++ ) 
            for (int j = 1; j <= n; j ++ )
            {
                if (d[i][j] > d[i][k] + d[k][j])
                {
                    d[i][j] = d[i][k] + d[k][j];
                    pos[i][j] = k;
                }
            }
        }
    }
    if (res == 0x3f3f3f3f) cout << "No solution." << endl;
    else{
        for (int i = 1; i < cnt; i ++ )
            cout << path[i] << " ";
    }
    return 0;
}

acwing345.牛站

d [ a + b ] [ i ] [ j ] d[a+b][i][j] d[a+b][i][j]表示经过边数为 a + b a+b a+b时从 i i i j j j的最短路径。
d [ a + b ] [ i ] [ j ] = m i n { d [ a ] [ i ] [ k ] + d [ b ] [ k ] [ j ] } d[a+b][i][j]=min\{d[a][i][k]+d[b][k][j]\} d[a+b][i][j]=min{d[a][i][k]+d[b][k][j]}

如果一个一个枚举的话,需要 O ( n 4 ) O(n^4) O(n4),要一个一个往上加边数,从1到2到3…到k再枚举 i , j , k i,j,k i,j,k,时间复杂度太大过不了
往上加的时候,发现可以利用快速幂(二进制)的思想来处理最外层的循环往上加边数的限制,从而将时间复杂度降成 ( n 3 l o g n ) (n^3logn) (n3logn)
g g g数组,也就是一开往里读入的图,通过不断地倍增,在需要的时候,也就是 k & 1 = 1 k\&1=1 k&1=1时,把 r e s res res g g g数组,进行folyd相加
例如,通过倍增,把 g [ i ] [ j ] g[i][j] g[i][j]更新成了恰好经过a条边的最短路,而此时 r e s [ i ] [ j ] res[i][j] res[i][j]数组更新成了恰好经 b b b条边的最短路,这样把两者放在一块开始更新, r e s [ i ] [ j ] res[i][j] res[i][j]就会更新成了从i到j恰好经过 a + b a+b a+b条边的最短路
在更新前会开个 t e m p temp temp临时数组来存暂时要求的 r e s res res数组,因为是把两个数组旧的 r e s res res g g g相加,来更新新 r e s res res数组
不能用更新出来的 r e s res res值,来再更新自己,那样就违背了边数限制,所以先用 t e m p temp temp数组来临时存答案,求完了再把值赋给 r e s res res,从而更新它。

#include <iostream>
#include <cstring>
#include <map>
using namespace std;

const int N = 210, M = 110;
int d[N][N];
int res[N][N];
int k, n, m, s, e;
void mul(int a[][N], int b[][N], int c[][N])
{
    static int temp[N][N];
    memset(temp, 0x3f, sizeof temp);
    for (int k = 1; k <= n; k ++ )
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
            {
                temp[i][j] = min(temp[i][j], b[i][k] + c[k][j]);
            }
    memcpy(a, temp, sizeof temp);
}
void qmi()
{
    memset(res, 0x3f,sizeof res);
    for (int i = 1; i <= n; i ++ ) res[i][i] = 0;
    while (k)
    {
        if (k & 1) mul(res, res, d);
        mul(d, d, d);
        k >>= 1;
    }
}
int main()
{
    cin >> k >> m >> s >> e;
    map<int, int>ids; 
    memset(d, 0x3f, sizeof d);
    if (!ids.count(s)) ids[s] = ++ n;
    if (!ids.count(e)) ids[e] = ++ n;
    for (int i = 1; i <= m; i ++ )
    {
        int a, b, c;
        cin >> c >> a >> b;
        if (!ids.count(a)) ids[a] = ++ n;
        if (!ids.count(b)) ids[b] = ++ n;
        a = ids[a], b= ids[b];
        d[a][b] = d[b][a] = min(d[a][b], c);
    }
    qmi();
    cout << res[ids[s]][ids[e]];
    return 0;
}

http://www.kler.cn/a/525856.html

相关文章:

  • 基于Flask的旅游系统的设计与实现
  • deepseek R1的确不错,特别是深度思考模式
  • EasyExcel写入和读取多个sheet
  • JVM栈溢出线上环境排查
  • AboutDialog组件的功能和用法
  • MySQL通过binlog恢复数据
  • 【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】1.23 数据工厂:高级初始化模式解析
  • 【letta】The Letta Platform LETTA平台
  • 脚本运行禁止:npx 无法加载文件,因为在此系统上禁止运行脚本
  • 71-《颠茄》
  • 知识库管理系统助力企业实现知识共享与创新价值的转型之道
  • Rust语言进阶之filter用法实例(九十四)
  • 青少年编程与数学 02-008 Pyhon语言编程基础 06课题、字符串
  • SpringBoot 日志与配置文件
  • 智能家居环境监测系统设计(论文+源码)
  • 【Pandas】pandas Series cumprod
  • mysql重学(一)mysql语句执行流程
  • 【AI论文】Transformer^2: 自适应大语言模型
  • 数据库备份、主从、集群等配置
  • 【信息系统项目管理师-选择真题】2009上半年综合知识答案和详解
  • 【游戏设计原理】94 - 解决问题的方法
  • 赚钱的究极认识
  • INCOSE需求编写指南-附录 D: 交叉引用矩阵
  • Vscode编辑器下 Markdown无法显示图片
  • Docker小游戏 | 使用Docker部署RPG网页小游戏
  • mysql_init和mysql_real_connect的形象化认识