首页 > 其他分享 >图上的环和最长路

图上的环和最长路

时间:2024-05-08 20:34:16浏览次数:20  
标签:std int 负环 leq 图上 vector 最长 dis

1. 有向图找环

HDOJ 3342 Legal or Not https://acm.hdu.edu.cn/showproblem.php?pid=3342

题意:

给一个 \(N\) 个点 \(N\) 条边的有向图,第 \(i(1 \leq M)\) 条边从 \(a_i\) 连向 \(b_i\) ,询问该图是否无环。

\(2 \leq N, M \leq 100, 0 \leq a_i, b_i \leq N - 1\)

题解:

建图然后使用 Toposort ,如果没有剩余的图,则整个图无环。否则剩余的图是一个环。

std::vector<std::vector<int> > adj(N + 1);
std::vector<int> ind(N + 1);
for (int i = 1; i <= M; i++) {
    int u, v; std::cin >> u >> v;
    u++; v++;
    adj[u].push_back(v);
    ind[v]++;
}
std::queue<int> que;
for (int i = 1; i <= N; i++) if (ind[i] == 0) que.push(i);

std::queue<int> S;
while (!que.empty()) {
    int x = que.front();
    que.pop();
    S.push(x);
    for (auto y : adj[x]) {
        if (--ind[y] == 0)
            que.push(y);
    }
}
std::cout << (S.size() == N ? "YES" : "NO") << "\n";

时间复杂度 \(O(N + M)\) 。

HDU 1325 Is It A Tree? https://acm.hdu.edu.cn/showproblem.php?pid=1325

题意:

给一个有向图,判断是否是一棵树。

题解:

先用并查集判断是否图是连通图。

然后若整个图有且仅有一个节点的入度为 0 ,其他节点的入度为 1 。则是一棵树。


时间复杂度 \(O(N + M)\) 。

2. 无向图找环

http://poj.org/problem?id=3895

3. 最小环

有向图最小环:

无向图最小环:

暴力:

Dijikstra:

Floyd:

HDOJ 1599 https://acm.hdu.edu.cn/showproblem.php?pid=1599

HDOJ 6005 https://acm.hdu.edu.cn/showproblem.php?pid=6005

POJ 1734 http://poj.org/problem?id=1734

4. 正环/负环

正环即反权图上找负环。负环跑 Bellman-ford 迭代轮数 \(\geq N\) 。

https://www.luogu.com.cn/problem/P3385

题意:
给一个 \(n\) 个点 \(m\) 条边的有向图。询问是否存在从 \(1\) 出发能到达的负环。

\(1 \leq n \leq 2 \times 10^{3}, 1 \leq m \leq 3 \times 10^{3}, 1 \leq u, v \leq n, -10^{4} \leq w \leq 10^{4}, 1 \leq T \leq 10\)

题解:

从 \(1\) 跑 Bellman-ford 的第 \(N\) 轮,如果依然有边可以被松弛,则从 \(1\) 出发存在负环。

一轮就可以拿出负环的证明:

负环被确定后。每个点到源点的距离一定小于上一轮的距离。否则这个点不在负环上。

    int n, m; std::cin >> n >> m;
    std::vector<std::array<int, 3> > E;
    for (int i = 1; i <= m; i++) {
        int u, v, w; std::cin >> u >> v >> w;
        if (w >= 0) {
            E.push_back({u, v, w});
            E.push_back({v, u, w});
        } else {
            E.push_back({u, v, w});
        }
    }
    int M = E.size();
    std::vector<ll> dis(n + 1, llinf);
    dis[1] = 0;
    for (int i = 1; i < n; i++) {
        for (int j = 0; j < M; j++) {
            int u = E[j][0], v = E[j][1], w = E[j][2];
            if (dis[u] == llinf) continue;
            if (dis[u] + w < dis[v]) {
                dis[v] = dis[u] + w;
            }
        }
    }
    std::vector<int> negative(n + 1);
    for (int j = 0; j < M; j++) {
        int u = E[j][0], v = E[j][1], w = E[j][2];
        if (dis[u] == llinf) continue;
        if (dis[u] + w < dis[v]) {
            dis[v] = dis[u] + w;
            negative[v] = 1;
        }
    }
    int ok = 0;
    for (int i = 1; i <= n; i++) ok |= negative[i] > 0;
    std::cout << (ok ? "YES" : "NO") << "\n";

时间复杂度 \(O(nm)\) 。

5. DAG 最长路

P1807 最长路 https://www.luogu.com.cn/problem/P1807

题意:

给一个有向无环带权图,询问节点 \(1\) 到节点 \(n\) 的最长距离。

\(1 \leq n \leq 1500, 1 \leq m \leq 5 \times 10^{4}, 1 \leq u, v \leq n, -10^{5} \leq w \leq 10^{5}\)

题解:

先 \(O(n + m)\) 对 DAG 的反权图 TopoSort ,然后在 Topo 序上完成最短路的松弛操作。

结果的负数即最长路。

    int n, m; std::cin >> n >> m;
    std::vector<std::vector<std::array<int, 2> > > adj(n + 1);
    std::vector<int> ind(n + 1);
    for (int i = 1; i <= m; i++) {
        int u, v, w; std::cin >> u >> v >> w;
        adj[u].push_back({v, -w});
        ind[v]++;
    }
    std::queue<int> S, L;
    for (int i = 1; i <= n; i++) if (ind[i] == 0) S.push(i);
    while(!S.empty()) {
        int x = S.front();
        S.pop();
        L.push(x);
        for (auto info : adj[x]) {
            int y = info[0];
            if (!--ind[y])
                S.push(y);
        }
    }
    std::vector<ll> dp(n + 1, llinf);
    dp[1] = 0;
    while (!L.empty()) {
        int u = L.front();
        L.pop();
        if (dp[u] == llinf) continue;
        for (auto info : adj[u]) {
            int v = info[0], w = info[1];
            dp[v] = std::min(dp[v], dp[u] + w);
        }
    }
    std::cout << (dp[n] == llinf ? -1 : -dp[n]) << "\n";

时间复杂度 \(O(n + m)\) 。

6. 非 DAG 最长路

反权图上跑 Bellman-ford 。

https://atcoder.jp/contests/abc061/tasks/abc061_d

题意:

给一个一般图,询问 1 到 n 的最长路。

如果没有负环,从 1 跑反权图的 Bellman-ford 。

如果 1 在一个负环内,拿出这个负环。

基于这个负环,迭代出负环可达的点,若可达 n 。则最长路无限长。

    int N, M; std::cin >> N >> M;
    std::vector<std::array<int, 3> > E(M + 1);
    for (int i = 1; i <= M; i++) {
        int u, v, c; std::cin >> u >> v >> c;
        E[i] = {u, v, c};
    }
    std::vector<std::array<int, 3> > anti_E(E.begin(), E.end());
    for (int i = 1; i <= M; i++) anti_E[i][2] *= -1;
    
    std::vector<ll> dist(N + 1, llinf);
    dist[1] = 0;
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= M; j++) {
            int u = anti_E[j][0], v = anti_E[j][1], c = anti_E[j][2];
            if (dist[u] == llinf) continue;
            if (dist[u] + c < dist[v]) {
                dist[v] = dist[u] + c;
            }
        }
    }
    
    std::vector<int> negative(N + 1);
    std::vector<int> can(N + 1);
    for (int j = 1; j <= M; j++) {
        int u = anti_E[j][0], v = anti_E[j][1], c = anti_E[j][2];
        if (dist[u] == llinf) continue;
        if (dist[u] + c < dist[v])
            negative[v] = can[v] = 1;
    }
    for (int i = 1; i <= N - 1; i++) {
        for (int j = 1; j <= M; j++) {
            int u = anti_E[j][0], v = anti_E[j][1], c = anti_E[j][2];
            if (dist[u] == llinf) continue;
            if (can[u])
                can[v] = 1;
        }
    }
    
    if (can[N]) std::cout << "inf\n";
    else std::cout << -dist[N] << "\n";

标签:std,int,负环,leq,图上,vector,最长,dis
From: https://www.cnblogs.com/zsxuan/p/18180073

相关文章

  • leetCode 128. 最长连续序列
    给定一个未排序的整数数组nums,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。请你设计并实现时间复杂度为O(n)的算法解决此问题。示例1:输入:nums=[100,4,200,1,3,2]输出:4解释:最长数字连续序列是[1,2,3,4]。它的长度为4。示例2:输入:nums=[0,3,7,......
  • 最长子序列
    为了解决这个问题,我们可以使用滑动窗口的方法。滑动窗口可以让我们在不需要嵌套循环的情况下遍历序列中所有的连续子序列。以下是这个方法的步骤:初始化两个指针start和end,都指向序列的开始。初始化当前和current_sum为0。移动end指针,每次移动都将end指向的值加到c......
  • 力扣1218.最长定差子序列
    题目给你一个整数数组arr和一个整数difference,请你找出并返回arr中最长等差子序列的长度,该子序列中相邻元素之间的差等于difference。​ 子序列是指在不改变其余元素顺序的情况下,通过删除一些元素或不删除任何元素而从arr派生出来的序列。解题思路​ 动态规划1.常......
  • 2024-05-04:用go语言,给定一个起始索引为0的字符串s和一个整数k。 要进行分割操作,直到字
    2024-05-04:用go语言,给定一个起始索引为0的字符串s和一个整数k。要进行分割操作,直到字符串s为空:选择s的最长前缀,该前缀最多包含k个不同字符;删除该前缀,递增分割计数。如果有剩余字符,它们保持原来的顺序。在操作之前,可以修改字符串s中的一个字符为另一个小写英文字母。在最佳情......
  • leetcode算法热题--最长连续序列
    题目给定一个未排序的整数数组nums,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。请你设计并实现时间复杂度为O(n)的算法解决此问题。示例1:输入:nums=[100,4,200,1,3,2]输出:4解释:最长数字连续序列是[1,2,3,4]。它的长度为4。示例2:输入:nums=......
  • leetcode算法热题--最长连续序列
    题目给定一个未排序的整数数组nums,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。请你设计并实现时间复杂度为O(n)的算法解决此问题。示例1:输入:nums=[100,4,200,1,3,2]输出:4解释:最长数字连续序列是[1,2,3,4]。它的长度为4。示例2:输入:nums=......
  • NlogN 求最长不下降子序列(LIS)
    最长不下降子序列是一道非常经典的题目,我们假设题目如下:有一个数组$a$,现在要从中抽取一个严格上升的子序列(子序列就是你可以从原序列里删除一些数,保留一部分数得到的新数列)(严格上升也就是不能相等只能递增),现在要求出这个子序列最长有多长?举例来说,假设有一个数组a=[10,9,......
  • 最长递增子序列
    https://leetcode.cn/problems/longest-increasing-subsequence/?envType=study-plan-v2&envId=top-interview-150classSolution:deflengthOfLIS(self,nums:List[int])->int:dp=[1]*len(nums)foriinrange(1,len(nums)):......
  • 洛谷题单指南-动态规划2-P1439 【模板】最长公共子序列
    原题链接:https://www.luogu.com.cn/problem/P1439题意解读:求最长公共子序列的长度。解题思路:1、O(n^2)的方法:动态规划设两个排列为a,b设dp[i][j]表示a[1~i]与b[1~j]的最长公共子序列长度根据公共子序列结尾是否包含a[i]、b[j]是否相等分情况讨论:如果a[i]==b[j]  则结尾......
  • 无重复字符的最长子串
    给定一个字符串s,请你找出其中不含有重复字符的最长子串的长度。示例1:输入:s="abcabcbb"输出:3解释:因为无重复字符的最长子串是"abc",所以其长度为3。示例2:输入:s="bbbbb"输出:1解释:因为无重复字符的最长子串是"b",所以其长度为1。示例3:输入:s......