Search and graph theory training improvement for algorithm competition preparation, summer training camp training

Table of contents

1. DFS and BFS

1.1.DFS depth-first search

1.2.BFS breadth-first search

2. Tree and graph traversal: topological sorting

3. Shortest path

 3.1. Dijkstra algorithm

3.2. Bellman Algorithm

3.3. SPFA Algorithm

3.4. Multi-source-sink shortest path Floy algorithm

4. Minimum spanning tree

4.1. Prim's Algorithm

4.2. Kruskal Algorithm

5. Bipartite graph: coloring method, Hungarian algorithm

5.1. Dyeing method

5.2. Hungarian Algorithm


1. DFS and BFS

1.1.DFS depth-first search

Depth-First Search (DFS) is an algorithm for traversing or searching a tree or graph. It starts from the starting point, follows a path to the deepest node, then backtracks to the previous node, and continues to explore the next path until all nodes have been visited.

DFS uses a stack to store the nodes to be visited, it will push the starting point into the stack, and then repeat the following steps until the stack is empty:

  1. Pop the top node of the stack.

  2. If the node is the target node, the search ends.

  3. Otherwise, mark the node as visited, and push all its unvisited neighbor nodes into the stack according to some rules (such as alphabetical order).

The advantage of using DFS is that its space complexity is relatively small , because it only needs to store the nodes on one path. However, if the searched graph or tree is very large, DFS may get stuck in an infinite loop or run for a long time. Furthermore, DFS does not necessarily find the optimal solution because it only explores one path, not all possible paths.

Therefore, in practical applications, it is necessary to select an appropriate search algorithm according to the requirements of specific problems. For example, if you need to find the shortest path, a Breadth-First Search (BFS) algorithm might be more appropriate.

842. Arrange numbers :

Given an integer n, arrange the numbers 1~n in a row, there will be many ways to arrange them.

Now, please output all the permutation methods in lexicographical order.

#include<iostream>
using namespace std;
const int N = 7;
int n;
int path[N];
bool st[N];
void dfs(int u)
{
    if(u == n)
    {
        for(int i = 0;i < n; i++)
            printf("%d ", path[i]);
        printf("\n");
        return;
    }
    
    for(int i = 1;i <= n; i++)
    {
        if(!st[i])
        {
            path[u] = i;
            st[i] = true;
            dfs(u+1);
            st[i] = false;
        }
    }
}
int main()
{
    scanf("%d", &n);
    
    dfs(0);
    
    return 0;
}

843.n-queen problem :

The n-queen problem refers to placing n queens on the nn international chessboard so that the queens cannot attack each other, that is, any two queens cannot be in the same row, column or slash.

Now given an integer n, please output all chessboard layouts that meet the conditions.

Act 1

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 20;
int n;
char g[N][N];
bool col[N], dg[N], udg[N];
void dfs(int u)
{
    if(u == n)
    {
        for(int i = 0;i < n; i++)
            puts(g[i]);
        printf("\n");
        return;
    }
    
    fot(int i = 1;i <= n; i++)
    {
        if(!col[i] && !dg[u + i] && !udg[n - u + i])
        {
            path[u] = i;
            col[i] = dg[u + i] = udg[n - u + i] = true;
            dfs(u+1);
            col[i] = dg[u + i] = udg[n - u + i] = false;
            g[u][i] = '·';
        }
    }
}
int main()
{
    scanf("%d", &n);
    for(int i = 0;i < n; i++)
    {
        for(int j = 0;j < n; j++)
            g[i][j] = '·';
    }
    
    dfs(0);
    
    return 0;
}

Method 2 :

#include<iostream>
using namespace std;
const int N = 20;
int n;
char g[N][N];
bool row[N], col[N], dg[N * 2], udg[N * 2];
void dfs(int x, int y, int s)
{
    if(y == n)
    {
        y = 0;
        x++;
    }
    
    if(x == n)
    {
        if(s == n)
        {
            for(int i = 0;i < n; i++)
                puts(g[i]);
            puts("");
        }
        return;
    }
    
    //不放皇后
    dfs(x, y+1, s);
    
    //放皇后
    if(!row[x] && !col[y] && !dg[x+y] && !udg[x-y+n])
    {
        g[x][y] = 'Q';
        row[x] = col[y] = dg[x+y] = udg[x-y+n] = true;
        dfs(x, y+1, s+1);
        row[x] = col[y] = dg[x+y] = udg[x-y+n] = false;
        g[x][y] = '.';
    }
}
int main()
{
    scanf("%d", &n);
    for(int i = 0;i < n; i++)
    {
        for(int j = 0;j < n; j++)
        {
            g[i][j] = '.';
        }
    }
    
    dfs(0, 0, 0);
    
    return 0;
}

1.2.BFS breadth-first search

Breadth-First Search (BFS) is an algorithm for traversing or searching a tree or graph. It starts from the starting point and first visits all nodes directly adjacent to the starting point, then visits the adjacent nodes of these nodes, and so on, until all nodes have been visited.

BFS uses a queue to store nodes to be visited, it pushes the starting point into the queue, and then repeats the following steps until the queue is empty:

  1. Pop the head node of the queue.

  2. If the node is the target node, the search ends.

  3. Otherwise, mark the node as visited, and push all its unvisited neighbor nodes into the queue according to some rules (such as alphabetical order).

The advantage of using BFS is that it can guarantee to find the target node in the least time, because it searches from the starting point from near to far according to the distance. In addition, BFS is also able to handle directed acyclic graph (DAG) and graph cases. However, if the searched graph or tree is very large, BFS may require a large space to store the nodes in the queue, so the space complexity is large.

Therefore, in practical applications, it is necessary to select an appropriate search algorithm according to the requirements of specific problems. For example, if you need to find the shortest path with a depth-first search, a depth-first search algorithm might be more appropriate.

844. Maze walk :

Given a two-dimensional array of n*m integers to represent a maze, the array only contains 0 to 1, where 0 represents the path that can be walked and the wall that cannot pass. Initially, there is a person located at the upper left corner (1, 1), and it is known that the person can move one position in any direction of up, down, left, or right each time.

Excuse me, how many times does the person need to move at least from the upper left corner to the lower right corner (n, m).

The data guarantees that the numbers at (1, 1) and (n, m) are 0, and there must be at least one path.

Program code 1 :

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int N = 110;
typedef pair<int, int> PII;
int g[N][N];
int d[N][N];
int n, m;
int bfs()
{
    queue<pair<int, int>> q;
    q.push({0, 0});
    memset(d, -1, sizeof(d));
    d[0][0] = 0;
    
    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
    while(q.size())
    {
        PII t = q.front();
        q.pop();
        for(int i = 0;i < 4; i++)
        {
            int x = t.first + dx[i], y = t.second + dy[i];
            if(x >= 0 && x < n && y >= 0 && y < m && g[x][y] == 0 && d[x][y] == -1)
            {
                d[x][y] = d[t.first][t.second] + 1;
                q.push({x, y});
            }
        }
    }
    
    return d[n-1][m-1];
}
int main()
{
    scanf("%d%d", &n, &m);
    
    for(int i = 0;i < n; i++)
        for(int j = 0;j < m; j++)
            scanf("%d", &g[i][j]);
    
    cout << bfs() << endl;
    return 0;
}

print path code :

#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
typedef pair<int, int> PII;
const int N = 110;
int n, m;
int g[N][N];
int d[N][N];
int prev[N][N];
PII q[N * N];
int bfs()
{
    int hh = 0, tt = 0;
    q[0] = {0, 0};
    
    memset(d, -1, sizeof(d));
    d[0][0] = 0;
    
    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
    
    while(hh <= tt)
    {
        auto t = q[hh++];
        
        for(int i = 0;i < 4; i++)
        {
            int x = t.first + dx[i], y = t.second + dy[i];
            if(x >= 0 && x < n && y >= 0 && y < m && g[x][y] == 0 && d[x][y] == 0 && d[x][y] == -1)
            {
                d[x][y] = d[t.first][t.second] + 1;
                prev[x][y] = t;
                q[++tt] = {x, y};
            }
        }
    }
    
    int x = n - 1, y = m - 1;
    while(x || y)
    {
        cout << x << ' ' << y << endl;
        auto t = prev[x][y];
        x = t.first, y = t.second;
    }
    
    return d[n-1][m-1];
}
int main()
{
    scanf("%d%d", &n, &m);
    
    for(int i = 0;i < n; i++)
    {
        for(int j = 0;j < m; j++)
        {
            scanf("%d", g[i][j]);
        }
    }
    
    cout << bfs() << endl;
    
    return 0;
}

5 5

0 1 0 0 0

0 1 0 1 0

0 0 0 0 0

0 1 1 1 0

0 0 0 1 0

eight digits

A tree is a special kind of graph that belongs to the acyclic graph.

Graphs are divided into directed graphs and undirected graphs.

846. Center of gravity of tree :

Given a tree, the tree contains n nodes (numbered 1~n) and n-1 undirected edges.

Please find out the center of gravity of the tree, and output the maximum number of points in each connected block after removing the center of gravity.

Definition of center of gravity: center of gravity refers to a node in the tree. If the node is deleted and the maximum number of points in the remaining connected blocks is the smallest, then this node is called the center of gravity of the tree.

code :

#include<iostream>
using namespace std;
const int N = 100010, M = N * 2;
int n, m;
int h[N], e[M], ne[M], idx;
bool st[N];
int ans = N;
void add(int a, int b)
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}
int dfs(int u)
{
    st[u] = true;//标记一下
    
    int sum = 0, res = 0;
    
    for(int i = h[u];i != -1; i = ne[i])
    {
        int j = e[i];
        if(!st[j])
        {
            int s = dfs(j);
            res = max(res, s);
            sum += s;
        }
    }
    
    res = max(res, n - sum - 1);
    
    ans = min(ans, res);
    return sum + 1;
}
int main()
{
    scanf("%d", &n);
    
    memset(h, -1, sizeof(h));
    
    for(int i = 0;i < n - 1; i++)
    {
        int a, b;
        cin >> a >> b;
        add(a, b), add(b, a);
    }
    
    dfs(1);
    
    cout << ans << endl;
    
    return 0;
}

847. Hierarchy of points in a graph :

Given a directed graph with n vertices and m edges, multiple edges and self-loops may exist in the graph.

The length of all sides is 1, and the number of points is 1~n.

Please find the shortest distance from point 1 to point n. If you cannot reach point n from point 1, output -1.

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int h[N], e[N], ne[N], idx;
int d[N], q[N];
void add(int a, int b)
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}
int bfs()
{
    int hh = 0, tt = 0;
    q[0] = 1;
    
    memset(d, -1, sizeof(d));
    d[1] = 0;
    
    while(hh <= tt)
    {
        int t = q[hh++];
        
        for(int i = h[t];i != -1; i = ne[i])
        {
            int j = e[i];
            if(d[j] == -1)
            {
                d[j] = d[t] + 1;
                q[++tt] = j;
            }
        }
    }
    
    return d[n];
}
int main()
{
    cin >> n >> m;
    
    memset(h, -1, sizeof(h));
    
    for(int i = 0;i < m; i++)
    {
        int a, b;
        cin >> a >> b;
        
        add(a, b);
    }
    
    cout << bfs() << endl;
    
    return 0;
}

2. Tree and graph traversal: topological sorting

848. Topological Sequences of Directed Graphs

Given a directed graph with n vertices and m edges, multiple edges and self-loops may exist in the graph.

Please input any topological sequence of the directed graph, if the topological sequence does not exist, output -1.

If a sequence A composed of all points in the graph satisfies: for each edge (x, y) in the graph, x appears before y in A, then A is said to be a topological sequence of the graph.

Directed Acyclic Graph - Topological Graph

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int h[N], e[N], ne[N], idx;
int q[N], d[N];
void add(int a, int b)
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}
bool topsort()
{
    int hh = 0, tt = 0;
    
    for(int i = 1;i <= n; i++)
    {
        if(!d[i])
            q[++tt] = i;
    }
    
    while(hh <= tt)
    {
        int t = q[hh++];
        
        for(int i = h[t];i != -1; i = ne[i])
        {
            int j = e[t];
            d[j]--;
            if(d[j] == 0)
            {
                q[++tt] = j;
            }
        }
    }
    
    return tt == n - 1;
}
int main()
{
    cin >> n >> m;
    
    memset(h, -1, sizeof(h));
    
    for(int i = 0;i < n; i++)
    {
        int a, b;
        cin >> a >> b;
        add(a, b);
        d[b]++;
    }
    
    if(topsort())
    {
        for(int i = 0;i < n; i++)
            printf("%d ", q[i]);
        puts("");
    }
    else
        puts("-1");
    
    cout << << endl;
    
    return 0;
}

3. Shortest path

The shortest path problem is the problem of finding the shortest path from a start point to an end point in a given directed or undirected graph. This problem has a wide range of applications in computer science and applied mathematics, such as routing algorithms, traffic control, and circuit design, where the shortest path problem needs to be solved.

There are two main methods to solve the shortest path problem: Dijkstra algorithm and Bellman-Ford algorithm.

Dijkstra's algorithm is a greedy algorithm. It is based on the principle of graph theory. By continuously updating the shortest distance from the starting point to each node, the shortest path from the starting point to the end point is finally obtained. The basic idea of ​​the algorithm is to start from the starting point, select a node closest to the starting point each time, and update the distance from the starting point to each node. By repeating this process, the shortest path from the start point to the end point can be finally obtained.

The Bellman-Ford algorithm is a dynamic programming algorithm that can handle graphs with negatively weighted edges. The basic idea of ​​the algorithm is to gradually update the shortest distance from the starting point to each node by performing multiple relaxation operations on all nodes. In each relaxation operation, the algorithm checks to see if there is a node whose distance can be shortened by the path of other nodes, and if so, updates the node's distance. Through multiple relaxation operations, the algorithm can find the shortest path from the start point to the end point.

In addition to these two algorithms, there are other methods to solve the shortest path problem, such as the Floyd-Warshall algorithm and the A* algorithm. Different algorithms are suitable for different types of graphs and different application scenarios, and the appropriate algorithm needs to be selected according to the specific situation.

uTools_1689583511271

 3.1. Dijkstra algorithm

Dijkstra's Algorithm is an algorithm for solving the shortest path problem, which finds the shortest paths from a starting point to all other nodes in a weighted graph.

The basic idea of ​​the algorithm is to start from the starting point, select the node with the shortest distance as the transit point each time, and update the distance of the neighbor nodes of this node until all nodes have been visited or there is no reachable node.

The specific implementation process is as follows:

  1. Initialization: Set the distance of the starting point to 0, and the distance of other nodes to infinity.

  2. Select a transit point: start from the starting point, select the node closest to the starting point as a transit point, and update the distance of the node to the distance from the starting point to the node.

  3. Update distance: For the neighbor nodes around the transit point, if the distance from the starting point to the neighbor node is shorter than the previous distance, the distance to update the neighbor node is the distance from the starting point to the neighbor node.

  4. Repeat steps 2 and 3 until all nodes have been visited or there are no reachable nodes.

  5. Output result: Finally, the shortest path from the starting point to all nodes is obtained.

The time complexity of Dijkstra's algorithm is O(n^2), where n is the number of nodes. If heap optimization is used, the time complexity can be optimized to O(m log n), where m is the number of edges.

849.Dijkstra seeks the shortest path I :

Given a directed graph with n points and m edges, there may be multiple edges and self-loops in the graph, so the edge weights are all positive.

Please find the shortest distance from point 1 to point n. If you cannot go from point 1 to point n, output -1.

3 3

1 2 2

2 3 1

1 3 4

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 510;
int n, m;
int g[N][N];
int dist[N][N];
bool st[N];
int dijkstra()
{
    memset(dist, 0x3f, sizeof(dist));
    dist[1] = 0;
    
    for(int i = 0;i < n; i++)
    {
        int t = -1;
        for(int j = 1;j <= n; j++)
            if(!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;
            
            st[t] = true;
            
            for(int j = 1;j <= n; j++)
                dist[j] = min(dist[j], dist[t] + g[t][j]);    
    }
    
    if(dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}
int main()
{
    cin >> n >> m;
    
    memset(g, 0x3f, sizeof(g));
    
    while(m--)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        
        g[a][b] = min(g[a][b], c);
        
    }
    
    int t = dijkstra();
    
    printf("%d\n", t);
    
    return 0;
}

Dijkstra finds the minimum short circuit II :

Heap-optimized version of Dijkstra's algorithm

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dist[N];
bool st[N];
void add(int a,int b,int c)
{
    e[idx] = b;
    w[idx] = c;
    ne[idx] = h[a];
    h[a] = idx++;
}
int dijkstra()
{
    memset(dist, 0x3f, sizeof(dist));
    dist[1] = 0;
    
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, 1});
    
    while(heap.size())
    {
        auto t = heap.top();
        heap.pop();
        
        int ver = t.second, distance = t.first;
        if(st[ver]) continue;
        
        for(int i = h[ver];i != -1; i = ne[i])
        {
            int j = e[i];
            if(dist[j] > distance + w[i])
            {
                dist[j] = distance + w[i];
                heap.push({dist[j], j});
            }
        }
    }
    
    if(dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}
int main()
{
    scanf("%d%d", &n, &m);
    
    memset(h, -1, sizeof(h));
    
    while(m--)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);
    }
    
    int t = dijkstra();
    
    printf("%d\n", t);
    
    return 0;
}

3.2. Bellman Algorithm

Bellman-Ford Algorithm

for loop n times, each time looping all edges

dist[b] = min(dist[b], dist[a]) relaxation operation

dist[b] ≤ dist[a] + w The triangle inequality has negative weight sides, so it is difficult to hold

853. Shortest path with limit on the number of edges :

Given a directed graph with n points and m edges, there may be multiple edges and self-loops in the graph, and the edge weight may be negative.

Please find the shortest distance from point 1 to point n passing through at most k edges. If it is impossible to go from point 1 to point n, output impossible.

Note: There may be negative weight loops in the graph.

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 510, M = 10010;
int n, m, k;
iny dist[N], backup[N];
struct Edge
{
    int a, b, w;
}edges[M];
int bellman_ford()
{
    memset(dist, 0x3f, sizeof(dist));
    dist[1] = 0;
    
    for(int i = 0;i < k; i++)
    {
        memcpy(bakcup, dist, sizeof(backup));
        
        for(int j = 0;j < m; j++)
        {
            int a = edges[j].a, b = edges[i].b, w = edges[i].w;
            dist[b] = min(dist[b], backup[a] + w);
        }
    }
    
    if(dist[n] > 0x3f3f3f3f / 2) return -1;
    return dist[n];
}
int main()
{
    scanf("%d%d%d", &n, &m, &k);
    
    for(int i = 0;i < m; i++)
    {
        int a, b, w;
        scanf("%d%d%d", &a, &b, &w);
        edges[i] = {a, b, w};
    }
    
    int t = bellman_ford();
    
    if(t == -1)
        puts("impossible");
    else printf("%d\n", t);
    
    return 0;
}

3.3. SPFA Algorithm

851.spfa seeking the shortest path

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 510, M = 10010;
int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dist[N];
bool st[N];
void add(int a, int b, int c)
{
    e[idx] = b;
    w[idx] = c;
    ne[idx] = h[a];
    h[a] = idx++;
}
int spfa()
{
    memset(dist, 0x3f, sizeof(dist));
    dist[1] = 0;
    
    queue<int> q;
    q.push(1);
    st[1] = true;
    
    while(q.size())
    {
        int t = q.front();
        q.pop();
        
        st[t] = false;
        
        for(int i = h[t];i != -1; i = ne[i])
        {
            int j = e[i];
            if(dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                if(!st[j])
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }
    
    if(dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}
int main()
{
    scanf("%d%d%d", &a, &b, &c);
    
    memset(h, -1, sizeof(h));
    
    while(m--)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);
    }
    
    int t = spfa();
    
    if(t == -1)
        puts("");
    else
        printf("%d\n", t);
    
    return 0;
}

852.spfa judges negative ring :

Given a directed graph with n points and m edges, there may be multiple edges and self-loops in the graph, and the edge weight may be negative.

Please judge whether there are negative weight loops in the graph.

3 3

1 2 -1

2 3 4

3 1 -4

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 10010;
int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dist[N], cnt[N];
bool st[N];
void add(int a, int b, int c)
{
    e[idx] = b;
    w[idx] = c;
    ne[idx] = h[a];
    h[a] = idx++;
}
int spfa()
{
    queue<int> q;
    q.push(1);
    st[1] = true;
    
    while(q.size())
    {
        int t = q.front();
        q.pop();
        
        st[t] = false;
        
        for(int i = h[t];i != -1;i = ne[i])
        {
            int j = e[i];
            if(dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                cnt[j] = cnt[t] + 1;
                
                if(cnt[j] >= n) return true;
                if((!st[j]))
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }
    
    return false;
}
int main()
{
    scanf("%d%d", &n, &m);
    
    memset(h, -1, sizeof(h));
    
    while(m--)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);
    }
    
    if(spaf())
        printf("Yes\n");
    else
        printf("No\n");
    return 0;
}

3.4. Multi-source-sink shortest path Floy algorithm

for(int k = 1;k <= n; k++)
{
    for(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]);
        }
    }
}

854.Floyd seeks the shortest path :

Given a directed graph with n points and m edges, there may be multiple edges and closed loops in the graph, and the edge weight may be negative.

Given k queries, each query contains two integers x and y, indicating the shortest distance from point x to point y, if the path does not exist, output "impossible"

Data guarantees that there are no negative weight loops in the graph.

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e4 + 10;
int n, m, Q;
int d[N][N];
void floyd()
{
    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][k], d[k][j]);
            }
        }
    }
}
int main()
{
    scanf("%d%d%d", &n, &m, &Q);
    
    for(int i = 1;i <= n; i++)
    {
        for(int j = 1;j <= n; j++)
        {
            if(i == j)
                d[i][j] = 0;
            else
                d[i][j] = INF;
        }
    }
    
    while(m--)
    {
        int a, b, w;
        scanf("%d%d%d", &a, &b, &w);
        
        d[a][b] = min(d[a][b], w);
    }
    
    floyd();
    
    while(Q--)
    {
        int a, b;
        scanf("%d%d",&a, &b);
        
        if(d[a][b] == INF)
            puts("impossible");
        else
            printf("%d\n", d[a][b]);
    }
    return 0;
}

4. Minimum spanning tree

Two algorithms for minimum spanning tree:

  1. Prim algorithm (Prim)

  2. Kruskal algorithm (Kruskal)

4.1. Prim's Algorithm

Naive version of Prim , similar to Dijkstra's algorithm

The main part of Dijkstra's algorithm:

int dijkstra()
{
    memset(dist, -1, sizeof(dist));
    dist[1] = 0;
    
    for(int i = 0;i < n - 1; i++)
    {
        int t = -1;
        for(int j = 1;j <= n; j++)
        {
            if(!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;
        }
        
        for(int  j = 1;j <= n; j++)
        {
            dist[j] = min(dist[j], dist[t] + g[t][j]);
        }
        
        st[t] = true;
    }
    
    if(dist[n] == 0x3f3f3f3f)
        return -1;
    return dist[n];
}

Prim's algorithm idea

dist[i] <- positive infinity

for(i = 0;i < n; i++)
{
    t-> find the point with the closest set distance
    Use t to update the distance of other points to the set,
    st[t] = true;
}

858.Prim algorithm to find the minimum spanning tree :

Given an undirected graph with n points and m edges, there may be multiple edges and self-loops in the graph, and the edge weight may be negative.

Find the sum of the tree edge weights of the minimum spanning tree, and output impossible if the minimum spanning tree does not exist.

Given an undirected graph G=(V, E) with side weights, where V represents the set of points in the graph, E represents the set of edges in the graph, n=[V], m=[E]

The undirected connected subgraph composed of all n vertices in V and n-1 edges in E is called a spanning tree of G, and the spanning tree with the smallest sum of edge weights is called undirected The minimal connected graph of graph G.

4 5

1 2 1

1 3 2

1 4 3

2 3 2

3 4 4

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
cosnt int N = 1e4 + 10;
int n, m;
int g[N][N];
int dist[N];
bool st[N];
int prim()
{
    memset(dist, 0x3f, sizeof(dist));
    
    int res = 0;
    for(int i = 0;i < n; i++)
    {
        int t = -1;
        for(int j = 1;j <= n; j++)
        {
            if(!st[t] && (t == -1 || dist[t] > dist[j]))
                t = j;
            
            if(i && dist[t] == INF)
                return INF;
            
            if(i)
                res += dist[t];
            
            for(int j = 1;j <= n; j++)
                dist[j] = min(dist[j], g[t][j]);
            
            st[t] = true;
        }
    }
    
    return true;
}
int main()
{
    scanf("%d%d", &n, &m);
    
    memset(g, 0x3f, sizeof(g));
    
    while(m--)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        g[a][b] = g[b][a] = min(g[a][b], c);
    }
    
    int t = prim();
    
    if(t == INF)
        puts("impossible");
    else
        printf("%d\n", t);
    
    return 0;
}

Heap-optimized Prim

Not much is actually used in the competition.

4.2. Kruskal Algorithm

The basic idea of ​​Kruskal algorithm:

  1. Sort all edges in ascending order of weight

  2. Enumerate each edge a, b, weight c If a, b are not connected, add this edge to this set

859. Kruskal algorithm for minimum spanning tree :

4 5

1 2 1

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e4 + 10;
int n, m;
int p[N];
struct Edge
{
    int a, b, w;
    
    bool operator< (const Edge &W)const
    {
        return w < W.w;
    }
}edges[N];
int find(int x)
{
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
    
}
int main()
{
    scanf("%d%d", &n, &m);
    
    for(int i = 0;i < m; i++)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        edges[i] = {a, b, w};
    }
    
    sort(edges, edges + m);
    
    for(int i = 1;i <= n; i++)
        p[i] = i;
    
    int res = 0, cnt = 0;
    for(int i = 0;i < m; i++)
    {
        int a = edges[i].a, b = edges[i].b, w = edges[i].w;
        
        a = find(a), b = find(b);
        if(a != b)
        {
            p[a] = b;
            res += w;
            cnt++;
        }
    }
    
    if(cnt < n - 1)
        puts("impossible");
    else
        printf("%d\n", res);
    
    return 0;
}

5. Bipartite graph: coloring method, Hungarian algorithm

A bipartite graph is bipartite if and only if there are no odd cycles in the graph.

A bipartite graph, also known as a bipartite graph, is a special model in graph theory. Let G=(V, E) be an undirected graph, if it can be divided into two disjoint subsets (A, B) according to the vertex V, and each edge (i, j) in the graph is associated with two Vertices i and j belong to these two different vertex sets (i∈A, j∈B), then the graph G is a bipartite graph. In simple terms, the vertex set V can be divided into two mutually disjoint subsets, and the two vertices attached to each edge in the graph belong to these two mutually disjoint subsets, and the vertices in the two subsets not adjacent.

Bipartite graphs have maximum matching and minimum matching problems. A matching in a bipartite graph means that any two edges in the edge set do not attach to the same vertex. A maximum matching means that under the currently completed matching, it is impossible to increase The number of matching sides is increased by using the side that has not completed the matching. The maximum matching is the matching with the largest number of sides among all extremely large matchings.

Tell if it is a bipartite graph:

  1. Coloring method O(n + m)

  2. Hungarian algorithm O(mn), the actual running time is generally much less than O(mn)

5.1. Dyeing method

The bipartite graph coloring method is a method for judging bipartite graphs, which can be divided into connected graph judgment and disconnected graph judgment1.

The idea of ​​coloring is as follows:

  1. Initially all vertices are uncolored.

  2. Randomly take an uncolored vertex u and dye it a color (assumed to be 0).

  3. Take the node v connected to it, if v is not dyed, then dye v to a color different from u (assumed to be 1), if v has been dyed, then judge whether the colors of u and v are the same, the same indicates that the dyeing failed, The graph is not bipartite, end.

  4. Traverse all nodes, repeat steps).

  5. A connected graph requires only one dfs coloring, while a disconnected graph requires multiple dfs colorings.

for(int i = 1;i <= n; i++)
{
    if(v uncolored)
        dfs(i);
}

860. Coloring method to determine bipartite graph

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10, M = 2e5 + 10;
int n, m;
int h[N], e[M], ne[M], idx;
int color[N];
void add(int a, int b)
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}
bool dfs(int u, int father, int c)
{
    color[u] = c;
    for(int i = h[u];i != -1; i = ne[i])
    {
        int j = e[i];
        if(color[j] == -1)
        {
            if(!dfs(j, 3 - c)) return false;
        }
        else if(color[j] == c) return false;
    }
    
    return true;
}
int main()
{
    scanf("%d%d", &n, &m);
    
    memset(h, -1, sizeof(h));
    
    while(m--)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b), add(b, a);
    }
    
    bool flag = true;
    for(int i = 1;i <= n; i++)
    {
        if(!color[i])
        {
            if(!dfs(i, 1))
            {
                flag = false;
                break;
            }
        }
    }
    
    if(flag) puts("Yes");
    else puts("No");
    
    return 0;
}

Subject: Imprisonment of Criminals

5.2. Hungarian Algorithm

The Hungarian Algorithm is a combinatorial optimization algorithm for solving task assignment problems in polynomial time, and drove the later primal-dual approach.

861. Maximum Matching of Bipartite Graphs

Given a bipartite graph, the left half contains n1 points (numbered 1~n1), the right half contains n2 points (numbered 1~n2), and the bipartite graph contains m edges in total.

The data guarantees that the two endpoints of any edge cannot be in the same part.

Please find the maximum matching number of the bipartite graph.

Given a bipartite graph G, in a subgraph M of G, any two edges of the edge set (E) of M are not attached to the same vertex, which is called a matching.

The set of matches that contains the most edges among all matches is called the maximum number of matches of the bipartite graph.

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 510, M = 1e5 + 10;
int n1, n2, m;
int h[N], e[M], ne[M], idx;
int match[N];
bool st[N];
void add(int a, int b)
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}
bool find(int x)
{
    for(int i = h[x];i != -1; i = ne[i])
    {
        int j = e[i];
        if(!st[j])
        {
            st[j] = true;
            if(match[j] == 0 || find(match[j]))
            {
                match[j] = x;
                return true;
            }
        }
    }
    
    return false;
}
int main()
{
    scanf("%d%d%d", &n1, &n2, &m);
    
    memset(h , -1, sizeof(h));
    
    while(m--)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b);
    }
    
    int res = 0;
    for(int i = 1; i <= n1; i++)
    {
        memset(st, false, sizeof(st));
        if(find(i)) res++;
    }
    
    printf("%d\n", res);
    
    return 0;
}

2 2 4

1 1

1 2

2 1

2 2

Guess you like

Origin blog.csdn.net/Williamtym/article/details/132268628