zoukankan      html  css  js  c++  java
  • 【内功】基础算法——图论

    1. 如何判断一个图是二分图(染色问题)leetcode 886 possible bipartition

    题解:我们用一个color数组标记点的颜色,然后对每一个点做bfs,如果两个点间有边,并且另外一个点没有被染色,就把另外一个点染色成相反色,如果另外一个点有颜色,而且颜色和当前结点相同,那么肯定不是二分图。

     1 class Solution {
     2 public:
     3     bool possibleBipartition(int N, vector<vector<int>>& dislikes) {
     4         N1 = N;
     5         const int edges = dislikes.size();
     6         if (edges == 0) {return true;}
     7         for (auto& p : dislikes) {
     8             stDis.insert(make_pair(p[0], p[1]));
     9         }
    10         vector<int> color(N + 1, -1);
    11         for (int i = 1; i < N+1; ++i) {
    12             if (color[i] == -1 && !bfs(i, color)) {
    13                 return false;
    14             }
    15         }
    16         return true;
    17     }
    18     bool bfs(int p, vector<int>& color) {
    19         queue<int> que;
    20         que.push(p);
    21         color[p] = 1;
    22         while (!que.empty()) {
    23             int node = que.front();
    24             que.pop();
    25             for (int i = 1; i <= N1; ++i) {
    26                 if (i == node) { continue; }
    27                 pair<int, int> e = make_pair(min(i, node), max(i, node));
    28                 if (stDis.find(e) != stDis.end()) {
    29                     if (color[i] == -1) {
    30                         color[i] = 1 - color[node];
    31                         que.push(i);
    32                     } else if (color[i] == color[node]) {
    33                         return false;
    34                     }
    35                 }
    36             }
    37         }
    38         return true;
    39     }
    40     int N1;
    41     set<pair<int, int>> stDis;
    42 };
    leetcode 886 bfs

    这个解法巨慢, 1700+ms。看有没有更快的方法。

    solution 里面给了 dfs 的方法,80ms

    就是先建图,因为是DAG,注意双边。然后从一个没有染过色的点开始,dfs染色,如果相邻结点有染色,判断是否冲突,如果相邻结点没有染色,就把它染成相反色。

     1 //dfs 方法 建图的时候,注意是双边都要加,这是DAG
     2 class Solution {
     3 public:
     4     bool possibleBipartition(int N, vector<vector<int>>& dislikes) {
     5         vector<int> temp(N + 1, -1);
     6         color = temp;
     7         m = dislikes.size();
     8         edges.resize(N + 1);
     9         for (const auto& p : dislikes) {
    10             edges[p[0]].push_back(p[1]);
    11             edges[p[1]].push_back(p[0]);
    12         }
    13         for (int i = 1; i < N+1; ++i) {
    14             if (color[i] == -1 && !dfs(i, 0)) {
    15                 return false;
    16             }
    17         }
    18         return true;
    19     }
    20     bool dfs(int node, int c) {
    21         if (color[node] != -1) { return color[node] == c; }
    22         color[node] = c;
    23         for (const auto& p : edges[node]) {
    24             if (!dfs(p, 1 - c)) {
    25                 return false;
    26             }
    27         }
    28         return true;
    29     }
    30     vector<int> color;
    31     vector<vector<int>> edges;
    32     int m;
    33 };
    leetcode 886 dfs

     重新写了一个bfs,也能80ms过,看来是第一个方法不好,没有双向建图

     1 //bfs 方法 建图的时候,注意是双边都要加,这是DAG
     2 class Solution {
     3 public:
     4     bool possibleBipartition(int N, vector<vector<int>>& dislikes) {
     5         vector<int> temp(N + 1, -1);
     6         color = temp;
     7         m = dislikes.size();
     8         edges.resize(N + 1);
     9         for (const auto& p : dislikes) {
    10             edges[p[0]].push_back(p[1]);
    11             edges[p[1]].push_back(p[0]);
    12         }
    13         for (int i = 1; i < N+1; ++i) {
    14             if (color[i] != -1) { continue; }
    15             queue<int> que;
    16             que.push(i);
    17             color[i] = 0;
    18             while (!que.empty()) {
    19                 int node = que.front(); que.pop();
    20                 for (auto& p : edges[node]) {
    21                     if (color[p] == color[node]) {return false;}
    22                     if (color[p] == -1) {
    23                         color[p] = 1 - color[node];
    24                         que.push(p);
    25                     }
    26                 }
    27             }
    28         }
    29         return true;
    30     }
    31     vector<int> color;
    32     vector<vector<int>> edges;
    33     int m;
    34 };
    leetcode 886 bfs

    2. 拓扑排序 (leetcode 207)(算法见《算法竞赛入门指南》chp6 数据结构基础 6.4.3 拓扑排序)

    基本概念和定义:如果图中存在有向环,则不存在拓扑排序,反之则存在。我们把不包含有向环的有向图称为有向无环图(directed acyclic graph, DAG)。可以借助 dfs 完成拓扑排序:在访问完一个结点之后把它放在当前拓扑排序的首部。(想想为啥不是尾部)

    这里用到了一个 c 数组, c[u] = 0 表示从来没有访问过(从来没有调用过 dfs(u) );c[u] = 1 表示已经访问过,并且还递归访问过它的所有子孙(即dfs(u)曾经被调用过,并已经返回);c[u] = -1 说明正在访问(即递归调用dfs(u)正在栈帧中,尚未返回)。 

     1 #include <iostream>
     2 #include <cstring>
     3 #include <cstdio>
     4 #include <cstdlib>
     5 
     6 using namespace std;
     7 
     8 int c[100] = {0};
     9 int topo[100] = {0}, t;
    10 int g[105][105];
    11 int n; //n个结点
    12 
    13 bool dfs(int u) {
    14     c[u] = -1;
    15     for (int v = 0; v < n; ++v) {
    16         if (g[u][v]) {
    17             if (c[v] < 0) { return false; } //说明有环
    18             else if (!c[v] && !dfs(v)) {return false;}
    19         }
    20     }
    21     c[u] = 1;
    22     topo[--t] = u;
    23     return true;
    24 }
    25 
    26 bool toposort() {
    27     t = n;
    28     memset(c, 0, sizeof(c));
    29     for (int u = 0; u < n; ++u) {
    30         if (!c[u]) {
    31             if (!dfs(u)) {
    32                 return false;
    33             }
    34         }
    35     }
    36     return true;
    37 }
    38 
    39 int main(int argc, char *argv[]) {
    40     n = 5;
    41     memset(g, 0 , sizeof(g));
    42     g[0][1] = 1;
    43     g[0][3] = 1;
    44     g[3][4] = 1; 
    45     //g[4][3] = 1; 检测环
    46     bool ret = toposort();
    47     if (ret) {
    48         for (int i = 0; i < n; ++i) {
    49             printf("%d ", topo[i]);
    50         }
    51         printf("
    ");
    52     }
    53     if (!ret) {
    54         printf("no ans
    ");
    55     }
    56     return 0;    
    57 }
    View Code

     额外补充:如何判断toposort的唯一性,看是否多于一个结点的入度为 0。

    2019年1月20日,补充topologic sort 的 bfs 版本。算法流程如下:

    (1)建图(邻接链表) (2)计算入度 (3)找出所有入度为 0 的点加入队列 (4)开始bfs

     1 class Solution {
     2 public:
     3     vector<int> findOrder(int numCourses, vector<pair<int, int>>& prerequisites) {
     4         vector<vector<int>> g = initGraph(numCourses, prerequisites);
     5         vector<int> degree = calDegree(g);
     6         vector<int> ret(numCourses, 0);
     7         queue<int> que;
     8         for (int i = 0; i < degree.size(); ++i) {
     9             if (degree[i] == 0) {
    10                 que.push(i);
    11             }
    12         }
    13         for (int i = 0; i < numCourses; ++i) {
    14             if (que.empty()) {
    15                 return vector<int>();
    16             }
    17             int cur = que.front(); que.pop();
    18             ret[i] = cur;
    19             for (auto e : g[cur]) {
    20                 if (--degree[e] == 0) {
    21                     que.push(e);
    22                 }
    23             }
    24         }
    25         return ret;
    26     }
    27 private:
    28     vector<vector<int>> initGraph(int n, vector<pair<int, int>>& pre) {
    29         vector<vector<int>> g(n, vector<int>());
    30         for (auto e : pre) {
    31             int start = e.second, end = e.first;
    32             g[start].push_back(end);
    33         }
    34         return g;
    35     }
    36     vector<int> calDegree(vector<vector<int>>& g) {
    37         vector<int> d(g.size(), 0);
    38         const int n = g.size();
    39         for (auto e : g) {
    40             for (auto v : e) {
    41                 d[v]++;
    42             }
    43         }
    44         return d;
    45     }
    46 };
    View Code

    3. 最小生成树

    https://www.cnblogs.com/zhangwanying/p/9431761.html

    4. 最短路

    https://www.cnblogs.com/zhangwanying/p/9431761.html

    5. 如何在一个无向图上找环上的所有结点。(检测环可以直接Union Find)

    这个思路是从kickstart 2018 Round C 的 Problem A 里面来的。

    我们可以用bfs做,O(N)的算法复杂度,Kahn的算法思想。我们先把图上所有度为 1 的结点放进队列里面,然后从图上删除这个结点(就是从队列里面pop出来的时候,这个结点的度设置为 0)。删除了当前结点之后,我们遍历当前结点的没有访问过的相邻结点(此时相邻结点的度也应该减一),减一后看他们的度是否为 1,如果度为1,就把相邻结点放进队列里面。最后队列空了的时候,度不为 0 的点就全在环上。

  • 相关阅读:
    SQL SQL 连接 JOIN 例解。(左连接,右连接,全连接,内连接,交叉连接,自连接)[转]
    ADO.NET 1.基础(SqlCommand\ExecuteScalar\ExecuteReader\sqlDataAdapter)
    SQL 14.子查询
    winform 基础
    SQL – 12.索引 + 13.join
    判断是否为数字
    SQL 17.存储过程
    SQL 16.事务
    SQL 15.变量和流程控制
    SQL 18.触发器
  • 原文地址:https://www.cnblogs.com/zhangwanying/p/9900782.html
Copyright © 2011-2022 走看看