首页 > 其他分享 >图论学习笔记

图论学习笔记

时间:2024-08-31 10:52:15浏览次数:8  
标签:图论 Min int 短路 rightsquigarrow 笔记 st 学习 include

最短路

割边最短路

约定

\(1 \rightsquigarrow x\) 表示
\(T(1 \rightsquigarrow x)\) 表示最短路树 \(T\) 上 \(1 \rightsquigarrow x\) 的经过的边的集合。

思路

问题:给定一张无向正权图,对图上的每条边,求删去该边后 \(1 \rightsquigarrow n\) 的最短路。

首先求出 \(1 \rightsquigarrow n\) 的最短路的路径 \(P\),定义 \(len\) 表示 \(P\) 包含边的数量,从 \(1 \sim len\) 对 \(P\) 的每一条边进行编号。设 \(e\) 表示当前考虑的边 \((i, j)\)。

若 \(e \notin P\),删去 \(e\) 后对答案没有影响。

若 \(e \in P\),考虑从 \(1\) 出发到达 \(n\) 的最短路树 \(T_1\) 和 \(T_n\),要求 \(T_1, T_n\) 上 \(1 \rightsquigarrow n\) 的路径等于 \(P\)(最短路树并不是唯一的)。

枚举每一条边 \((u, v)\)。若 \(T_1(1 \rightsquigarrow u)\) 和 \(T_n(v \rightsquigarrow n)\) 均不包含 \(e\),即可用 \(w(T_1(1\rightsquigarrow u)) + w(u \rightarrow v) + w(T_n(v \rightsquigarrow n))\) 更新最短路,

设 \(l_u\) 表示最短路树 \(T_1\) 上 \(1 \rightsquigarrow u\) 与 \(P\) 第一条不相交的边,\(r_v\) 表示最短路树 \(T_n\) 上节点 \(v \rightsquigarrow n\) 与 \(P\) 第一条不重合的边。

\(l_u\) 与 \(r_v\) 时容易求的,首先,若 \((u, v) \in P\),那么 \(l_u = u\) 下一条边的编号,\(r_v = v\) 上一条边的编号。

而当 \((u, v) \notin P\) 时,\(l_v = \min(l_u, l_v), r_v = \max(r_u, r_v)\)(注意边是无向的)。

对于一条不在 \(P\) 上的边 \((u, v)\),若删去 \(l_u \rightsquigarrow r_v\) 中任一边,新的最短路就可能经过 \((u, v)\),否则,必然不经过 \((u, v)\)。

于是可以先预处理出删除 \(l_u \rightsquigarrow r_v\) 后可能的结果。用 multiset 来维护考虑删除 \(l_u \rightsquigarrow r_v\) 中任一边后新的最短路的最小值即可。

具体来说:定义两个数组 \(A, B\),\(A_i\) 用来记录 \(l_u = i\)(\(i\) 为 \(P\) 中边的编号) 的可能的最短路值,\(B_i\) 用来记录 \(r_v = i\) 时的最短路值,用 multiset 容器定义一个 \(Min\) 来维护考虑到 \(i\) 时可能的最短路。

当考虑到编号为 \(i\) 的边时,将 \(A_i\) 中数加入到 \(Min\) 中,考虑完后,将 \(B_i\) 中的数从 \(Min\) 中删去。这样就可以准确的维护删去 \(i\) 时的最短路。

例题

P2685 [TJOI2012] 桥
题意简述:给定一张自环和重边的无向正权图,求删去某条边,使最短路最大。
求该最短大小和删去边的方案。
模版题。

点击查看代码
/*
  --------------------------------
  |        code by FRZ_29        |
  |          code  time          |
  |          2024/08/31          |
  |           08:31:10           |
  |             星期六            |
  --------------------------------
                                  */

#include <algorithm>
#include <iostream>
#include <climits>
#include <cstring>
#include <cstdio>
#include <vector>
#include <ctime>
#include <queue>
#include <set>

using namespace std;

void RD() {}
template<typename T, typename... U> void RD(T &x, U&... arg) {
    x = 0; int f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    x *= f; RD(arg...);
}

const int N = 1e5 + 4, M = 2e5 + 5;

#define FI first
#define SE second
#define PB push_back
#define PII pair<int, int>
#define FILL(x, y) memset(x, y, sizeof(x))
#define PRINT(x) cout << #x << "=" << x << "\n"
#define LF(i, __l, __r) for (int i = __l; i <= __r; i++)
#define RF(i, __r, __l) for (int i = __r; i >= __l; i--)

struct Edge { int u, v, w; } ed[M];
int head[N], ver[M << 1], Next[M << 1], edge[M << 1], tot = 1;
int dis1[N], dis2[N], pre[N], len, ans;
int l[N], r[N], t[N], res;
multiset<int> Min;
vector<int> dp1[N], dp2[N];
bool vis[N], st[M << 1];
int n, m;

bool cmp1(int a, int b) {
    return dis1[a] < dis1[b];
}

bool cmp2 (int a, int b) {
    return dis2[a] < dis2[b];
}

void add(int u, int v, int w) {
    ver[++tot] = v, edge[tot] = w;
    Next[tot] = head[u], head[u] = tot;
}

void dijkstra(int s, int* dis) {
    dis[s] = 0;
    FILL(vis, false);
    priority_queue<PII, vector<PII>, greater<PII>> que;
    que.push({0, s});
    
    while (que.size()) {
        int u = que.top().SE;
        que.pop();
        if (vis[u]) continue;
        vis[u] = true;

        for (int i = head[u]; i; i = Next[i]) {
            int v = ver[i], w = edge[i];

            if (dis[v] > dis[u] + w) {
                dis[v] = dis[u] + w;
                if (s == 1) pre[v] = i;
                que.push({dis[v], v});
            }
        }
    }
}

int main() {
//    freopen("read.in", "r", stdin);
//    freopen("out.out", "w", stdout);
//    time_t st = clock();
    RD(n, m);
    FILL(dis1, 0x3f), FILL(dis2, 0x3f);

    LF(i, 1, m) {
        int u, v, w;
        RD(u, v, w);
        add(u, v, w), add(v, u, w);
        ed[i] = {u, v, w};
    }

    dijkstra(1, dis1), dijkstra(n, dis2);
    FILL(l, 0x3f);
    l[1] = 1;
    for (int i = n; i != 1; i = ver[pre[i] ^ 1]) len++, st[pre[i]] = st[pre[i] ^ 1] = true;
    for (int i = n, cnt = len + 1; i != 1; i = ver[pre[i] ^ 1], cnt--) l[i] = cnt, r[i] = cnt - 1;
    LF(i, 1, n) t[i] = i;
    sort(t + 1, t + n + 1, cmp1);

    LF(i, 1, n) {
        int u = t[i];
        for (int i = head[u]; i; i = Next[i]) {
            int v = ver[i], w = edge[i];
            if (st[i]) continue;
            if (dis1[u] + w == dis1[v] && l[v] > l[u]) l[v] = l[u];
        }
    }

    LF(i, 1, n) t[i] = i;
    sort(t + 1, t + n + 1, cmp2);
    LF(i, 1, n) {
        int u = t[i];
        for (int i = head[u]; i; i = Next[i]) {
            int v = ver[i], w = edge[i];
            if (st[i]) continue;
            if (dis2[u] + w == dis2[v] && r[v] < r[u]) r[v] = r[u];
        }
    }

    LF(i, 1, m) {
        if (st[i << 1]) continue;
        int u = ed[i].u, v = ed[i].v, w = ed[i].w;
        
        if (l[u] <= r[v]) {
            dp1[l[u]].PB(dis1[u] + w + dis2[v]);
            dp2[r[v]].PB(dis1[u] + w + dis2[v]);
        }
        if (l[v] <= r[u]) {
            dp1[l[v]].PB(dis1[v] + w + dis2[u]);
            dp2[r[u]].PB(dis1[v] + w + dis2[u]);
        }
    }

    LF(i, 1, len) {
        for (int x : dp1[i]) Min.insert(x);
        if (*Min.begin() > res) res = *Min.begin(), ans = 1;
        else if (*Min.begin() == res) ans++;
        for (int x : dp2[i]) Min.erase(Min.find(x));
    }

    if (res == dis1[n]) ans = m;
    printf("%d %d", res, ans);
//    printf("\n%dms", clock() - st);
    return 0;
}

/* ps:FRZ弱爆了 */

标签:图论,Min,int,短路,rightsquigarrow,笔记,st,学习,include
From: https://www.cnblogs.com/FRZ29/p/18389986

相关文章

  • .NET|--WPF|--笔记合集|--依赖项属性|--2.注册依赖项属性
    前言使用一个实例,其实分2步骤定义,实例化.但是依赖项属性为不能直接实例化,因为DependencyProperty类没有公开的构造函数,只能使用静态的DependencyProperty.Register方法创建DependencyProperty实例.DependencyProperty.Register源码//System.Windows.DependencyP......
  • .NET|--WPF|--笔记合集|--依赖项属性|--1.定义依赖项属性
    前言一般情况下,我们是不用定义依赖项属性的,更多的是直接使用即可.那么何时需要我们定义依赖项属性呢?1.设计自定义的WPF元素;2.为原本不支持数据绑定,动画等WPF功能的代码中,需要添加数据绑定,动画等WPF功能时.定义依赖项属性一般的类型来说,如果想要使用的......
  • .NET|--WPF|--笔记合集|--依赖项属性|--4.依赖项属性值优先级
    前言前几篇笔记讲到了依赖项属性的定义,注册等.接下来就该是依赖项属性的实战了.如果依赖项属性是一个主机的话,前几个步骤还在于组装这个主机,组装好了之后,就要开始使用了,是骡子是马,拉出来遛遛.但是一般任何事物在使用之前,都有一些注意事项,如果不了解这些注......
  • .NET|--WPF|--笔记合集|--依赖项属性|--3.属性包装器
    前言属性包装器的主要作用是将依赖属性的访问方式转换为标准的CLR属性访问方式,从而使代码更加简洁、直观,并提供一致性和更好的开发体验。通过属性包装器,开发者可以利用依赖属性的高级功能,同时保持代码的可读性和易用性。"属性包装器"在TextBlock源码中使用publicclass......
  • C++学习,指针空指针
    C++空指针,一个在几个标准库中定义的值为零的常量。如果没有分配的地址,将指针NULL分配给指针变量,指定为NULL的指针称为null指针。大多数操作系统上,不允许访问地址0的内存,因为该内存是由操作系统保留的。NULL指针是一个常量,其值为零,在几个标准库中定义,包括iostream。 示例:#i......
  • [Python办公]一文入门图论Graphs,轻松处理最短路径等问题!
            [Python办公]一文入门图论Graphs,轻松处理最短路径等问题!        图论是研究图这种数学结构的性质和应用的学科。图(Graphs)由节点(或顶点)和连接这些节点的边组成,它是一种强大的数据结构,广泛应用于各种领域。以下举例用最短距离来入门图论。入门问题: ......
  • python学习总结--面向对象
    1.面向对象(上)1.1定义面向对象编程:oop[objectorientedprogramming]是一种python的编程思路;面向过程:就是我们一开始学习的,按照解决问题的步骤去写代码【根据业务逻辑去写代码】,在思考问题的时候,首先分析'怎么按照步骤去实现'然后将问题解决拆解成若干个步骤,并将这些步骤对......
  • Hash哈希学习笔记
    概念:通过一个hash函数建立值与存储地址的关系原则:开小数组+冲突解决冲突越少,用时越少;可通过调整余数或优质的hash算法尽量使hash值分散,减少碰撞hash算法的构成:hash函数的初始化构造hash函数:典型的函数包括除余法H......
  • 深度强化学习算法(六)(附带MATLAB程序)
    深度强化学习(DeepReinforcementLearning,DRL)结合了深度学习和强化学习的优点,能够处理具有高维状态和动作空间的复杂任务。它的核心思想是利用深度神经网络来逼近强化学习中的策略函数和价值函数,从而提高学习能力和决策效率。一、关键算法分类1.1深度Q网络(DeepQ-Networ......
  • helm学习第四篇-微服务组件的加入
    微服务的组件也放进去—向外扩张要将Nacos服务添加到你已经包含了SpringBoot、Redis、MySQL和RocketMQ的HelmChart中,你可以按照以下步骤操作:注意!!:nacos好像只有helm文件的github仓库,没有helm的包地址仓库。所以一会思路:找到nacos的github仓库:nacos仓库......