ACM - 搜索与图论 - 基础(搜索 + 拓扑 + 最小生成树 + 最短路 + 二分图 + 欧拉路 + 最大流 + 其它)

news2024/11/17 3:07:36

搜索与图论

  • 一、搜索
    • 1、分治
      • 矩阵二分 / 普通二分 模板
      • 万能二分模板
    • 2、DFS
      • 例题1、AcWing 842. 排列数字
      • 例题2、AcWing 843. n-皇后问题
    • 3、BFS
      • 例题1、AcWing 844. 走迷宫 (入门经典)
      • 例题2、到达 "12345678x":AcWing 845. 八数码
  • 二、图论
    • 写在前面
    • 1、图的存储
    • 2、图的类型
    • 3、基于DFS的图算法
      • 模板框架
      • 树与图的深度优先遍历 AcWing846. 树的重心
    • 4、基于BFS的图算法
      • 模板框架
      • bfs求无权最短路:AcWing 847. 图中点的层次
      • 求拓扑序列 :AcWing 848. 有向图的拓扑序列
    • 5、最小生成树
      • (1)朴素版 prim 算法 - 稠密图
      • (2)Kruskal 算法 - 稀疏图
    • 6、最短路算法
      • (1) 单源最短路
        • ① 所有边权重都是正数
          • Ⅰ、 朴素 Dijkstra
          • Ⅱ、 堆优化版 Dijkstra
        • ② 存在负权边
          • Ⅰ、 bellman-ford(有边数限制的最短路)
          • Ⅱ、spfa
            • AcWing 851. spfa求最短路
            • AcWing 852. spfa判断负环
      • (2) 多源汇最短路 - Floyd
    • 7、二分图
      • (1) 判定二分图 : 染色法
      • (2) 求最大匹配 : 匈牙利算法
    • 8、欧拉路
    • 9、最大流
    • 10、其他
      • (1) 树的直径 - 树上距离最远的两点间的距离
        • 模板题 : AcWing 1207. 大臣的旅费

一、搜索

1、分治

矩阵二分 / 普通二分 模板

原题链接:https://leetcode-cn.com/problems/sorted-matrix-search-lcci/
在这里插入图片描述
① 矩阵二分 :

/*
矩阵二分:从左下角开始二分,如果小了说明需要把列向右移,如果大了,说明需要把行向上移
*/
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int r = matrix.size();
        if (r == 0) return false;
        int c = matrix[0].size();
        if (c == 0) return false;
        int i = r - 1, j = 0;
        while (i >= 0 && j < c) {
            if (matrix[i][j] == target) return true;
            else if (matrix[i][j] < target) ++ j;
            else -- i;
        }
        return false;
    }
};

② 暴力二分:

/*
暴力二分:直接二分查每一行,速度慢
*/
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int r = matrix.size();
        if (r == 0) return false;
        int c = matrix[0].size();
        if (c == 0) return false;
        for (int i = 0; i < r; ++ i) {
            if (matrix[i][0] <= target && matrix[i][c - 1] >= target) {
                if (judge(matrix, i, target)) return true;
            }
        }
        return false;
    }
    //普通二分模板:在matrix[row]中判断是否存在target
    bool judge(vector<vector<int>>& matrix, int row, int target) {
        int l = 0, r = matrix[row].size();
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if (matrix[row][mid] == target) return true;
            else if (matrix[row][mid] < target) l = mid + 1;
            else r = mid - 1;
        }
        return false;
    }
};

万能二分模板

AcWing 789. 数的范围
原题链接:https://www.acwing.com/problem/content/description/791/
在这里插入图片描述
在这里插入图片描述
思路
在这里插入图片描述

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 100010;

int a[N], n, m;

int get_l(int num) {
    int l = 0, r = n - 1;
    while (l < r) {
        int mid = l + r >> 1;
        if (a[mid] >= num) r = mid;
        else l = mid + 1;
    }
    //就算return r也是可以的,因为l == r才会跳出循环
    if (l < n && l >= 0 && a[l] == num) return l;
    else return -1;
}

int get_r(int num) {
    int l = 0, r = n - 1;
    while (l < r) {
        //check()为true是l设置为mid,则需要+1防止死循环
        int mid = l + r + 1 >> 1;
        if (a[mid] <= num) l = mid;  
        else r = mid - 1;
    }
    //同上,就算return r也是可以的
    if (l < n && l >= 0 && a[l] == num) return l;
    else return -1;
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; ++ i) cin >> a[i];
    
    while (m --) {
        int num;
        cin >> num;
        cout << get_l(num) << " " << get_r(num) << endl;
    }
    
    return 0;
}

2、DFS

例题1、AcWing 842. 排列数字

原题链接:https://www.acwing.com/problem/content/description/844/
在这里插入图片描述

#include<iostream>

using namespace std;

int ans[10], n;
bool st[10];

void dfs(int idx) {  // idx - 第几个数
    if (idx > n) {
        for (int i = 1; i <= n; ++ i) printf("%d ", ans[i]);
        printf("\n");
    }    
    else {
        for (int i = 1; i <= n; ++ i) {
            if (!st[i]) {
                st[i] = true;
                ans[idx] = i;
                dfs(idx + 1);
                st[i] = false;  //还原
            }
        }
    }
}

int main() {
    scanf("%d", &n);
    dfs(1);
    return 0;
}

例题2、AcWing 843. n-皇后问题

原题链接:https://www.acwing.com/problem/content/845/
在这里插入图片描述
在这里插入图片描述
注意
在处理对角线的时候,我们可以借助一次函数,即把两条对角线抽相成 y = ± x + b,移项得到:b = y - x 和 b = y + x,那么这里我们可以用截距 b 去判断两个皇后会不会被放在同一条对角线上。

当然,因为 y - x 可能为负,所以我们可以用 n + y - x 表示该截距 b,因为我们需要的只是判断是否在同一条对角线上,至于 b 的值是正是负不影响结果,而加上 n 后就可以直接用拿 b + n 映射到索引上,方便写代码。

代码

#include<iostream>

using namespace std;

int n;
bool col[10], p[20], unp[20], ans[10][10];

void dfs(int row) {  //递归每一行
    if (row > n) {
        for (int i = 1; i <= n; ++ i) {
            for (int j = 1; j <= n; ++ j) {
                if (ans[i][j]) printf("Q");
                else printf(".");
            }
            printf("\n");
        }
        printf("\n");
    }
    else {
        for (int c = 1; c <= n; ++ c) {   // 循环判断每一列是否能放皇后
        	// 该列以及两个对角线可以放皇后则进入if
            if (!col[c] && !p[n + c - row] && !unp[row + c]) {
                col[c] = p[n + c - row] = unp[row + c] = true;
                ans[row][c] = true;
                dfs(row + 1);
                //记得还原
                col[c] = p[n + c - row] = unp[row + c] = false;
                ans[row][c] = false;
            }
        }
    }
}

int main() {
    scanf("%d", &n);
    dfs(1);
    return 0;
}

3、BFS

例题1、AcWing 844. 走迷宫 (入门经典)

原题链接:https://www.acwing.com/problem/content/846/

在这里插入图片描述
在这里插入图片描述
思路

因为每走一步的权重是 1 ,那么在 bfs 的模式下,最先到达终点的那条路必定是最短路,而且路径的长短 == bfs 的第一个 while 循环次数 - 1。

代码

#include <iostream>

using namespace std;

const int N = 110; 

int n, m, g[N][N], ans;
bool st[N][N];  //判断坐标(x,y)是否已经走过
int hh = 0, tt = -1;  // 模拟队列的头指针和尾指针
int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};  //向上下左右走
pair<int, int> q[N * N];  //队列  

void bfs() {
    while (tt - hh + 1 != 0) {
        int length = tt - hh + 1;
    	while (length --) {
    		pair<int, int> temp = q[hh ++];
    		if (temp.first == n && temp.second == m) return;  //找到终点
    		for (int i = 0; i < 4; ++ i) {
    			int x = temp.first + dx[i], y = temp.second + dy[i];
    			if (x > 0 && x <= n && y > 0 && y <= m && !st[x][y] && !g[x][y]) {
    				q[++ tt] = make_pair(x, y);
    				st[x][y] = true;
    			}
    		}
    	}
    	++ ans;
    }
}

int main() {
	//freopen("D:\\in.txt", "r", stdin); 
	//freopen("D:\\out.txt", "w", stdout);
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++ i) {
		for (int j = 1; j <= m; ++ j) {
			scanf("%d", &g[i][j]); 
		}
	} 
	q[++ tt] = make_pair(1, 1);
	bfs();
	printf("%d\n", ans);
	return 0;
}

例题2、到达 “12345678x”:AcWing 845. 八数码

原题链接:https://www.acwing.com/problem/content/847/
在这里插入图片描述
在这里插入图片描述
思路

对于一个初始状态,x 有上下左右四种走法,而这四种走法在当下也只需要交换一次位置即可得到,那么将其中合法的走法入队(如果是曾经走过的状态就不必入队了),再判断下一波,直到走到最终态 “12345678x” 或者队列为空(意味着无路可走了)。

代码

#include<iostream>
#include<string>
#include<queue>
#include<unordered_map>

using namespace std;

string start, ending = "12345678x";
int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};

int bfs() {
    queue<string> q;
    unordered_map<string, int> book;
    q.push(start);
    int ans = 0;
    while (!q.empty()) {
        int length = q.size();
        while (length --) {
            string temp = q.front();
            q.pop();
            if (temp == ending) return ans;
            int idx = temp.find('x');  //返回'x'的下标
            int x = idx / 3, y = idx % 3; //获得在3*3的横纵坐标
            for (int i = 0; i < 4; ++ i) {  //上下左右四个方向
                int a = x + dx[i], b = y + dy[i]; 
                if (a >= 0 && a < 3 && b >= 0 && b < 3) {  //合法性
                    swap(temp[idx], temp[a * 3 + b]);  //必须先判断a、b,不然a*3+b可能会非法访问
                    // 只有没经历过的字符串才需要添加进去
                    if (book.find(temp) == book.end()) {  
                        book[temp] = 1;
                        q.push(temp);
                    }
                    swap(temp[idx], temp[a * 3 + b]);  //还原
                }
            }
        }
        ++ ans;
    }
    return -1;
}

int main() {
    char op[3];
    for (int i = 0; i < 9; ++ i) {
        scanf("%s", op);
        start += op[0];
    }
    cout << bfs() << endl;
    return 0;
}

二、图论

写在前面

可以说,树是一种特殊的图,而无向图也是一种特殊的有向图,所以对于一些树和无向图的储存和遍历,可以抽象成是对特殊有向图的遍历。

1、图的存储

  1. 邻接矩阵:适合稠密图(m ~ n ^ 2)
int g[N][N];   //g[a][b] 表示从点a出发到点b的边
  1. 邻接表:适合稀疏图(m ~ n )
// 对于每个点k,开一个单链表,存储k所有可以走到的点
//h[k]存储这个单链表的头结点
int h[N], e[N], ne[N], idx = 1;

// 添加一条边a->b
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ; 
    //ne[idx] == 0 表示第idx个结点的next指向null
}

2、图的类型

3、基于DFS的图算法

模板框架

//时间复杂度 O(n+m)O(n+m), n 表示点数,m 表示边数
int dfs(int u)
{
    st[u] = true; // st[u] 表示点u已经被遍历过
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j]) dfs(j);
    }
}

树与图的深度优先遍历 AcWing846. 树的重心

原题链接:https://www.acwing.com/problem/content/description/848/

在这里插入图片描述
在这里插入图片描述
思路

因为该图是无向图,而且没有说明一定是以 1 为根节点,所以换句话说,把任一个结点拎起来,都可以自然垂下成为一棵树。换而言之,在 main 函数里面,dfs()里面传的参数可以是 1 到 n 之间的任意一个,得到的 ans 也会是一样的。
在这里插入图片描述
因为我们最终想要的 ans 是去掉树的重心后,数量最少的所有连通分支中最多结点的连通分支。比如说,假如上面以 1 为根的树的重心是 4,那么去掉重心后就有 3 个连通分支如下图:
在这里插入图片描述
而此时,连通分支结点数 res 最多是 5 个结点,所以 ans 就是要求最小的 res。

分析完题目要求,那么这道题的做法就是去 dfs,把每一个结点都先假设成是树的重心,进而去求去掉这个重心的数量最多的连通分支,最后对每一个 res 求 min 就得到了 ans

所以这道题的关键就在于怎么求在重心 u 的前提下数量最多的连通分支。最直接的做法就是去求 max(所有连通分支的结点数),换句话说,如果知道所有连通分支的结点数,直接求 max 即可。

比如说,现在要来求以 4 为重心的各个连通分支的结点数,那么分别为:dfs(3)、dfs(6)、n - dfs(3) - dfs(6),而所谓的 n - dfs(3) - dfs(6)虽然结点 1 也是 4 的一个分支,但是因为是从 1 走向 4 ,所以 1 一定在 4 之前被遍历到,所以 1 一定会在 if ( ! st [ j ] ) 时排除在 if 之外,不会重复 dfs (1),这符合了 dfs 时每个点一般只走一次的原则。

综上。

代码

#include<iostream>

using namespace std;

const int N = 100010, M = 200010;  //边数最大可达到 2N

int h[N], e[M], ne[M], idx = 1;  //邻接表表示树
bool st[N];  //判断点是否已经遍历过
int ans = N, n; // ans最多也只能达到n,所以初始化为大于等于n即可

//增加a到b的边
void add(int a, int b) {
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx ++;
}

//对点u深搜,返回以u为根的树的结点总数
int dfs(int u) {
    st[u] = true;  //标记点u已经走过
    //res - 假设u为重心,结点数最多的连通分量的结点数目
    //sum - 以u为根节点的树的结点总数
    int res = 0, sum = 1; 
    for (int i = h[u]; i != 0; i = ne[i]) {  //迭代得到所有和u相连的结点
        int j = e[i];  //获得在树中的编号
        if (!st[j]) {  //如果该结点还没走过
            int s = dfs(j); //获得该连通分量的结点总数
            sum += s;    //累加以u为根结点的结点数
            res = max(res, s);  //求最多结点数的连通分量
        }
    }
    res = max(res, n - sum);
    ans = min(ans, res);
    return sum;
}

int main() {
    scanf("%d", &n);
    int a, b;
    for (int i = 1; i < n; ++ i) {
        scanf("%d%d", &a, &b);
        add(a, b);  //无向图,所以相当于两条边
        add(b, a);
    }
    dfs(4);  // 1 ~ n皆可,这表示这棵树以哪一个为根节点
    printf("%d\n", ans);
    return 0;
}

4、基于BFS的图算法

模板框架

//树与图的广度优先遍历

queue<int> q;
st[1] = true; // 表示1号点已经被遍历过
q.push(1);

while (q.size())
{
    int t = q.front();
    q.pop();

    for (int i = h[t]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j])
        {
            st[j] = true; // 表示点j已经被遍历过
            q.push(j);
        }
    }
}

bfs求无权最短路:AcWing 847. 图中点的层次

原题链接:https://www.acwing.com/problem/content/submission/849/

在这里插入图片描述
在这里插入图片描述
思路
一圈圈往外走。

代码

#include<iostream>
#include<queue>

using namespace std;

const int N = 100010;

int g[N], e[N], ne[N], idx = 1;  //邻接表
bool book[N];  //判断该点是否已经走过

//插入从a走到b的边
void insert(int a, int b) {
    e[idx] = b;
    ne[idx] = g[a];
    g[a] = idx ++;
}

int bfs(int n) {
    queue<int> q;
    q.push(1);  //从1出发
    book[1] = true;  //标记已经走过点1
    int ans = 0;
    while (!q.empty()) {
        int length = q.size();
        ++ ans;  //走了几层就说明最短路是几
        while (length --) {
            int a = q.front();
            q.pop();
            int aa = g[a];
            while (aa != 0) {
                if (e[aa] == n) return ans;  //走到n,此时路程最短
                else {
                    if (!book[e[aa]]) {  //没走过的点才放进队列
                        book[e[aa]] = true;
                        q.push(e[aa]);
                    }
                }
                aa = ne[aa];
            }
        }
    }
    return -1;  //走不到n
}

int main() {
    int n, m, a, b;
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; ++ i) {
        scanf("%d%d", &a, &b);
        insert(a, b);
    }
    printf("%d\n", bfs(n));
    return 0;
}

求拓扑序列 :AcWing 848. 有向图的拓扑序列

有向无环图必定可为拓扑图。
有环不可能是拓扑图(因为删减到最后是一个环,环没有一个入度为0的结点可以突破)。
无向图不可能是拓扑图(无向相当于有环)。

在这里插入图片描述在这里插入图片描述

/*
入度为0才会被放进ans,如果搜索时出现a指向b,而b放进过ans里面,这是不可能存在的情况。
因为ans里面都是入度为0的点,为a指向b说明b入度大于0,自相矛盾
所以其实不用判断放入队列时,该点是否走过
*/
#include <bits/stdc++.h> 

using namespace std;

#define getlen(array) {return (sizeof(array) / sizeof(array[0]));}
#define ll long long 
#define ull unsigned long long
#define PII pair<ll, ll>
#define MEM(x, y) memset(x, y, sizeof x)
#define rin int n; scanf("%d", &n)
#define rln ll n; scanf("%lld", &n)
#define rim int m; scanf("%d", &m)
#define rit int t; scanf("%d", &t)
#define ria int a; scanf("%d", &a)
#define sc scanf
#define pr printf

const int INF = 0x3f3f3f3f;
const int N = 100010; 
int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};

int ans[N], d[N], k = 0;
int h[N], ne[N], e[N], idx = 1;

void insert(int a, int b) {
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx ++;
}

void bfs(int n) {
	queue<int> q;
	//预先把入度为0的放进去
	for (int i = 1; i <= n; ++ i) {
		if (!d[i]) {
			ans[k ++] = i;
			q.push(i);
		}
	}
	//一圈一圈往下走
	while (!q.empty()) {
		int num = q.front();
		q.pop();
		for (int i = h[num]; i != -1; i = ne[i]) {
			int a = e[i];
			-- d[a];
    		if (!d[a]) {
    			q.push(a);
    			ans[k ++] = a;
    		}
		}
	}
}

int main() {
	//freopen("D:\\in.txt", "r", stdin); 
	//freopen("D:\\out.txt", "w", stdout);
	rin;
	rim;
	MEM(h, -1);
	for (int i = 0; i < m; ++ i) {
		rit;
		ria;
		insert(t, a);
		++ d[a];
	}
	
	bfs(n);
	
	if (k == n) {
		for (int i = 0; i < n; ++ i) {
			cout << ans[i] << " ";
		}
	}
	else cout << -1;
	return 0;
}

5、最小生成树

在这里插入图片描述

(1)朴素版 prim 算法 - 稠密图

AcWing 858. Prim算法求最小生成树

原题链接:https://www.acwing.com/problem/content/description/860/
在这里插入图片描述
在这里插入图片描述

#include<bits/stdc++.h>

using namespace std;

const int N = 510;
const int INF = 0x3f3f3f3f;

int n, m;
//dis[i]表示i点所能确定的长度最小的边,不同于最短路里到起始点的距离
int dis[N], g[N][N];  
bool st[N];

int prim() {
    dis[1] = 0;  //一个点本身没有距离
    int ans = 0;  //最终生成树的权重和
    for (int i = 0; i < n; ++ i) {
        int t = -1;  //每一次找到还未确定的点里面权重最小的点
        for (int j = 1; j <= n; ++ j) {
            if (!st[j] && (t == -1 || dis[j] < dis[t])) {
                t = j;
            }
        }
        if (dis[t] == INF) return INF;  //说明不是连通图
        st[t] = true;  //标记为已访问
        ans += dis[t];
        //更新点集
        for (int j = 1; j <= n; ++ j) {
            dis[j] = min(dis[j], g[t][j]);
        }
    }
    return ans;
}

int main() {
    cin >> n >> m;
    //初始化
    memset(dis, INF, sizeof dis);
    memset(g, INF, sizeof g);
    for (int i = 0; i < m; ++ i) {
        int a, b, w;
        cin >> a >> b >> w;
        g[a][b] = g[b][a] = min(w, g[a][b]);  //无向图 + 有重边
    }
    
    int ans = prim();
    if (ans == INF) cout << "impossible";
    else cout << ans;
    
    return 0;
}

(2)Kruskal 算法 - 稀疏图

AcWing 859. Kruskal算法求最小生成树

原题链接:https://www.acwing.com/problem/content/description/861/
在这里插入图片描述
在这里插入图片描述

#include<bits/stdc++.h>

using namespace std;

const int N = 100010, M = 200010;

//表示从a到b权重为w的边
struct edge{
    int a, b, w;
}es[M];

int n, m;
int p[N];  //表示图中节点根属于哪一个集合,其实就是并查集里的father

//找到根节点
int find(int a) {
    if (p[a] != a) return p[a] = find(p[a]);
    else return a;
}

bool cmp(edge a, edge b) {
    return a.w < b.w;
}

void kruskal() {
    for (int i = 1; i <= n; ++ i) p[i] = i; //初始化根节点
    sort(es, es + m, cmp);  //按照权重从小到大排序
    
    int ans = 0, cnt = 1;  //ans - 生成树权重和, cnt - 已经加入几个结点
    
    for (int i = 0; i < m; ++ i) {
        int a = es[i].a, b = es[i].b, w = es[i].w;
        int x = find(a), y = find(b);
        if (x != y) {
            ++ cnt;
            ans += w;
            p[x] = y;  // 不可以是p[a] = y
        }
    }
    if (cnt < n) cout << "impossible";
    else cout << ans;
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < m; ++ i) {
        int a, b, w;
        cin >> a >> b >> w;
        es[i] = {a, b, w};
    }
    
    kruskal();
    
    return 0;
}

6、最短路算法

在这里插入图片描述

(1) 单源最短路

① 所有边权重都是正数

Ⅰ、 朴素 Dijkstra

AcWing 849. Dijkstra求最短路 I

原题链接 : https://www.acwing.com/problem/content/851/

在这里插入图片描述

#include <bits/stdc++.h> 

using namespace std;

#define getlen(array) {return (sizeof(array) / sizeof(array[0]));}
#define ll long long 
#define ull unsigned long long
#define PII pair<ll, ll>
#define MEM(x, y) memset(x, y, sizeof x)
#define rin int n; scanf("%d", &n)
#define rln ll n; scanf("%lld", &n)
#define rim int m; scanf("%d", &m)
#define rit int t; scanf("%d", &t)
#define ria int a; scanf("%d", &a)
#define sc scanf
#define pr printf

const int INF = 0x3f3f3f3f;
const int N = 510; 
int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};

//dis 为各个点到起点的最短距离
int g[N][N], dis[N]; 
bool st[N];  //判断是否已经更新到最短距离

int dijkstra(int n) {
	dis[1] = 0;
	//迭代n次
	for (int i = 0; i < n; ++ i) {
		int t = -1;
		//找到当前还没更新成最短距离且距离起点最近的点
		for (int j = 1; j <= n; ++ j) {
			if (!st[j] && (t == -1 || dis[j] < dis[t]))
				t = j;
		}
		//标记为已访问
		st[t] = true;
		//用贪心的思想更新所有点的距离
		for (int j = 1; j <= n; ++ j) {
			dis[j] = min(dis[j], dis[t] + g[t][j]);
		}
	}
	
	if (dis[n] == INF) return -1;  //不存在从起点到终点的路径
	else return dis[n];
}

int main() {
	//freopen("D:\\in.txt", "r", stdin); 
	//freopen("D:\\out.txt", "w", stdout);
	rin;
	rim;
	MEM(g, INF);
	MEM(dis, INF);
	for (int i = 0; i < m; ++ i) {
		int a, b, c;
		sc("%d%d%d", &a, &b, &c);
		g[a][b] = min(g[a][b], c);  //因为有重边,所以需要取min
	}
	
	pr("%d", dijkstra(n));
	return 0;
}
Ⅱ、 堆优化版 Dijkstra

AcWing 850. Dijkstra求最短路 II

原题链接:https://www.acwing.com/problem/content/submission/code_detail/6234491/
在这里插入图片描述

#include <bits/stdc++.h> 

using namespace std;

#define getlen(array) {return (sizeof(array) / sizeof(array[0]));}
#define ll long long 
#define ull unsigned long long
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define MEM(x, y) memset(x, y, sizeof x)
#define rin int n; scanf("%d", &n)
#define rln ll n; scanf("%lld", &n)
#define rim int m; scanf("%d", &m)
#define rit int t; scanf("%d", &t)
#define ria int a; scanf("%d", &a)
#define sc scanf
#define pr printf

const int INF = 0x3f3f3f3f;
const int N = 200000; 
int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};


// w存边的权重,dis为各个点到起点的最短距离
int h[N], e[N], ne[N], w[N], dis[N];
int idx = 1;
bool st[N];  //判断是否已经遍历过


//插入从a到b权重为c的边
void insert(int a, int b, int c) {
	e[idx] = b;
	ne[idx] = h[a];
	w[idx] = c;
	h[a] = idx ++; 
}

int dijkstra(int n) {
	dis[1] = 0;
	priority_queue<PII, vector<PII>, greater<PII> > heap;  //小根堆
	heap.push({0, 1});
	while (!heap.empty()) {
	    // 获得当前堆内距离起点最近的点
		PII p = heap.top();
		heap.pop();
		int distance = p.first, var = p.second;
		if (st[var]) continue;	//如果该点已有最短距离过便跳过
		st[var] = true;  //标记为已最短
		//遍历与该点存在连边的点
		for (int i = h[var]; i != -1; i = ne[i]) {
			int j = e[i];
			//更新距离且放在堆内
			if (dis[j] > distance + w[i]) {
				dis[j] = distance + w[i];
				heap.push({dis[j], j});
			}
		}
	}	
	if (dis[n] == INF) return -1;  //不存在从起点到终点的路径
	else return dis[n];
}

int main() {
	//freopen("D:\\in.txt", "r", stdin); 
	//freopen("D:\\out.txt", "w", stdout);
	rin;
	rim;
	MEM(h, -1);
	MEM(dis, INF); 
	while (m --) {
		int a, b, c;
		sc("%d%d%d", &a, &b, &c);
		insert(a, b, c);
	}
	
	pr("%d", dijkstra(n));
	return 0;
}

② 存在负权边

Ⅰ、 bellman-ford(有边数限制的最短路)

AcWing 853. 有边数限制的最短路

原题链接:https://www.acwing.com/problem/content/description/855/
在这里插入图片描述

#include<bits/stdc++.h>

using namespace std;

const int N = 510, M = 10010;
const int INF = 0x3f3f3f3f;

//从a到b权重为w的边
struct edge{
    int a, b, w;
};

int n, m, k;
int dis[N], cpy[N];  //距离数组和备份数组
edge es[M];  //边集

int bellman_ford() {
    memset(dis, INF, sizeof dis);
    dis[1] = 0;
    for (int i = 0; i < k; ++ i) {   //最多走k次
        memcpy(cpy, dis, sizeof dis);  //用备份数组去更新距离,防止更新时二次更新
        for (int j = 0; j < m; ++ j) {
            int a = es[j].a, b = es[j].b, w = es[j].w;
            dis[b] = min(dis[b], cpy[a] + w);
        }
    }
    //INF / 2是为了防止类似于1到a和1到n都是正无穷,但是a到n是负距离,
    //导致从1到a再到n比1直接到n小,而更新了dis[n]
    if (dis[n] > INF / 2) return -1;
    else return dis[n];
}

int main() {
    cin >> n >> m >> k;
    for (int i = 0; i < m; ++ i) {
        int a, b, w;
        cin >> a >> b >> w;
        es[i] = {a, b, w};
    }
    
    int ans = bellman_ford();
    if (ans == -1) cout << "impossible";
    else cout << ans;
    
    return 0;
}
Ⅱ、spfa
AcWing 851. spfa求最短路

原题链接:https://www.acwing.com/problem/content/853/
在这里插入图片描述

#include<bits/stdc++.h>

using namespace std;

const int N = 100010;
const int INF = 0x3f3f3f3f;

int n, m;
int h[N], e[N], ne[N], w[N], dis[N];
int idx = 1;

void insert(int a, int b, int c) {
    e[idx] = b;
    ne[idx] = h[a];
    w[idx] = c;
    h[a] = idx ++;
}

int spfa() {
    dis[1] = 0;
    queue<int> q;
    q.push(1);
    while (!q.empty()) {
        int t = q.front();
        q.pop();
        for (int i = h[t]; i != -1; i = ne[i]) {
            int j = e[i];
            if (dis[j] > dis[t] + w[i]) {
                dis[j] = dis[t] + w[i];
                q.push(j);  //和bellman—ford不同的是,只将有变化的边放进队列
            }
        }
    }
    //和bellmam-ford同理
    if (dis[n] > INF / 2) return -1;
    else return dis[n];
}

int main() {
    memset(h, -1, sizeof h);
    memset(dis, INF, sizeof dis);
    cin >> n >> m;
    for (int i = 0; i < m; ++ i) {
        int a, b, c;
        cin >> a >> b >> c;
        insert(a, b, c);
    }
    int ans = spfa();
    if (ans == -1) cout << "impossible";
    else cout << ans;
    
    return 0;
} 
AcWing 852. spfa判断负环

原题链接:https://www.acwing.com/problem/content/854/
在这里插入图片描述

#include<bits/stdc++.h>

using namespace std;

const int N = 100010;
const int INF = 0x3f3f3f3f;

int n, m;
int h[N], e[N], ne[N], w[N], dis[N], cnt[N];
int idx = 1;

void insert(int a, int b, int c) {
    e[idx] = b;
    ne[idx] = h[a];
    w[idx] = c;
    h[a] = idx ++;
}

bool spfa() {
    dis[1] = 0;
    queue<int> q;
    for (int i = 1; i <= n; ++ i) {
        q.push(i);  //将所有点放入,因为可能从1出发到不了负环
        cnt[i] = 1;
    }
    while (!q.empty()) {
        int t = q.front();
        q.pop();
        if (cnt[t] > n) return true;  //存在负环
        for (int i = h[t]; i != -1; i = ne[i]) {
            int j = e[i];
            if (dis[j] > dis[t] + w[i]) {
                dis[j] = dis[t] + w[i];
                q.push(j);  //只将有变化的边放进队列
                cnt[j] = cnt[t] + 1;
            }
        }
    }
    return false;
}

int main() {
    memset(h, -1, sizeof h);
    memset(dis, INF, sizeof dis);
    cin >> n >> m;
    for (int i = 0; i < m; ++ i) {
        int a, b, c;
        cin >> a >> b >> c;
        insert(a, b, c);
    }
    if (spfa()) cout << "Yes";
    else cout << "No";
    
    return 0;
} 

(2) 多源汇最短路 - Floyd

AcWing 854. Floyd求最短路

原题链接:https://www.acwing.com/problem/content/856/

#include <bits/stdc++.h> 

using namespace std;

#define getlen(array) {return (sizeof(array) / sizeof(array[0]));}
#define ll long long 
#define ull unsigned long long
#define PII pair<ll, ll>
#define MEM(x, y) memset(x, y, sizeof x)
#define rin int n; scanf("%d", &n)
#define rln ll n; scanf("%lld", &n)
#define rim int m; scanf("%d", &m)
#define rit int t; scanf("%d", &t)
#define ria int a; scanf("%d", &a)
#define sc scanf
#define pr printf

const int INF = 0x3f3f3f3f;
const int N = 210; 
int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};

int n, m, k;
int dis[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) {
				dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
			}
		}
	}
}

int main() {
	//freopen("D:\\in.txt", "r", stdin); 
	//freopen("D:\\out.txt", "w", stdout);
	
	cin >> n >> m >> k;
	//初始化
	for (int i  = 1; i <= n; ++ i) {
		for (int j = 1; j <= n; ++ j) {
			if (i == j) dis[i][j] = 0;
			else dis[i][j] = INF;
		}
	}
	//输入每条边
	for (int i = 0; i < m; ++ i) {
		int a, b, w;
		cin >> a >> b >> w;
		dis[a][b] = min(dis[a][b], w);
	}
	//寻找最短路
	floyd();
	//输出每组询问
	while (k --) {
		int a, b;
		cin  >> a >> b;
		if (dis[a][b] > INF / 2) cout << "impossible" << endl;
		else cout << dis[a][b] << endl;
	}
	
	return 0;
}

7、二分图

在这里插入图片描述

二分图:当且仅当图中不含奇数环。
奇数环:边的数量是奇数的环。

HDU 2458 Kindergarten :二分图用匈牙利求最大独立集

(1) 判定二分图 : 染色法

AcWing 860. 染色法判定二分图

原题链接:https://www.acwing.com/problem/content/submission/code_detail/6332360/
在这里插入图片描述

#include <bits/stdc++.h> 

using namespace std;

#define MEM(x, y) memset(x, y, sizeof x)

const int INF = 0x3f3f3f3f;
const int N = 200010; 

int n, m;
int color[N];  // 0-未染色  1-白色  2-黑色
int h[N], e[N], ne[N], idx = 1;

void insert(int a, int b) {
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx ++;
}

bool dfs(int a, int b) {
	color[a] = b;  //染色
	for (int i = h[a]; i != -1; i = ne[i]) {
		int j = e[i];
		if (!color[j]) {  //还没染过色
			if (!dfs(j, 3 - b)) return false;
		}
		else if (color[j] == b)  //已经染过色,但是出现矛盾
		    return false;
	} 
	return true;
} 


int main() {
	cin >> n >> m;
	MEM(h, -1);
	for (int i = 0; i < m; ++ i) {
		int a, b;
		cin >> a >> b;
        // if (a == b) continue;  //自环也是奇数环,不可以continue
		insert(a, b);
		insert(b, a);
	}
	bool ans = true;
	//可能不是连通图,所以需要逐一判断逐块染色
	for (int i = 1; i <= n; ++ i) {
		if (!color[i]) {
			if (!dfs(i, 1)) {
				ans = false;
				break;
			}
		}
	}
	
	if (ans) cout << "Yes";
	else cout << "No";
	
	return 0;
}

(2) 求最大匹配 : 匈牙利算法

看似时间复杂度为O(nm),实际远远小于。

AcWing 861. 二分图的最大匹配

原题链接:https://www.acwing.com/problem/content/description/863/
在这里插入图片描述
在这里插入图片描述

#include<bits/stdc++.h>

using namespace std;

const int N = 510, M = 100010;

int n1, n2, m;
int h[N], e[M], ne[M], idx = 1;
int match[N];   //记录右边都是和左边的谁匹配了
bool st[N];   //每一趟,都需要确保每一个女生只被访问一次,相当于剪枝

void insert(int a, int b) {
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx ++;
}

//查询是否能找到与其匹配的
bool find (int a) {
    for (int i = h[a]; i != -1; i = ne[i]) {
        int j = e[i];
        if (!st[j]) {
            st[j] = true;  //标记已访问
            //要么从未匹配,要么可以通过易主原先的达到匹配
            if (!match[j] || find(match[j])) {
                match[j] = a;
                return true;
            }
        }
    }
    return false;
}

int main() {
    memset(h, -1, sizeof h);
    cin >> n1 >> n2 >> m;
    for (int i = 0; i < m; ++ i) {
        int a, b;
        cin >> a >> b;
        insert(a, b);
    }
    int cnt = 0;
    for (int i = 1; i <= n1; ++ i) {
        //记得初始化,st只用于每一趟,而不是全局
        memset(st, 0, sizeof st);  
        if (find(i)) ++ cnt;
    }
    cout << cnt;
    
    return 0;
}

8、欧拉路

9、最大流

10、其他

(1) 树的直径 - 树上距离最远的两点间的距离

一般地,距离最远的这两点均为叶节点。特殊地,一棵仅含两个点的树,此时的直径为根节点与另一个节点的距离,直径为1;仅含一个根节点的树的直径为0。

模板题 : AcWing 1207. 大臣的旅费

( 原题是第四届蓝桥杯省赛C++A组 / 第四届蓝桥杯省赛JAVAA组 )

原题链接:https://www.acwing.com/problem/content/1209/
在这里插入图片描述
在这里插入图片描述
做法
先从任意一点a进入,dfs更新其余结点到这个结点的距离(加上权重),再遍历一下dis数组找到和a距离最远的结点b,再从结点b进入去同样dfs一次更新其余结点到b的距离(加上权重),最后遍历dis数组,此时数组中的最大值就是树的直径.

证明
主要反证法.
上面的做法其实换句话说就是,虽然 a 是任意选的, 但是 y 必然是直径的一个端点.
此时再从 y 进入寻找距离最远的结点, 那么此时 dis 里面保存的数值最大的点必然是直径的另一个点.
现反证如下:
在这里插入图片描述
xy 不是真正的直径, 设真正的直径是 uv , 且 xp 为 1, vp为 2, 其他依次类推.

情况一: uv 与 xy 有直接交点.
由于 y 是距离 x 最远的结点, 所以有 1 + 4 >= 1 + 3, 所以 4 >= 3
故而 2 + 4 >= 2 + 3, 所以当 2 + 4 == 2 + 3, 那么有多条直径, 如果 2 + 4 > 2 + 3, 那么存在比预设的直径长的路径, 故而预设有误. 但无论哪种情况都无法否认 y 是直径的一个端点.

情况二: uv 和 xy 没有直接相交.
但是由于这是一棵树,所以从一个结点出发,可以到达任意一个结点,所以必然存在图中的 pq, 而且由于 1 + 4 >= 1 + 3 + 5 , 所以 4 >= 3 + 5, 所以 4 >= 5.
故而 2 + 3 + 4 >= 2 + 5。换句话说,以 y 为直径的端点才符合直径的定义。

当然, x 并不一定就是直径的另一个端点, 有可能存在另外比 xy 长的.

所以这个时候就需要再从 y 出发, 更新除 y 的所有结点到 y 的距离, 此时的那个最大距离就是直径.

#include <iostream>
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <vector>
#include <map>
#include <set>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <climits>
#include <unordered_set>
#include <unordered_map>

using namespace std;

#define getlen(array) {return (sizeof(array) / sizeof(array[0]));}
#define ll long long 
#define ull unsigned long long
#define PII pair<ll, ll>
#define MEM(x, y) memset(x, y, sizeof x)
#define rin int n; scanf("%d", &n)
#define rln ll n; scanf("%lld", &n)
#define rit int t; scanf("%d", &t)
#define ria int a; scanf("%d", &a)
#define sc scanf
#define pr printf

const int INF = 0x3f3f3f3f;
const int N = 100010;

struct edge {
	int id, weight;
};

vector<edge> e[N]; 
int dis[N];

void dfs (int idx, int fa, int d) {
    dis[idx] = d;
    int length = e[idx].size();
    for (int i = 0; i < length; ++ i) {
        edge ed = e[idx][i];
        if (ed.id != fa) {
            dfs(ed.id, idx, d + ed.weight);
        }
    }
}

int main() {
	//freopen("D:\\in.txt", "r", stdin); 
	//freopen("D:\\out.txt", "w", stdout);
	rin;
	int a, b, c;
	for (int i = 1; i < n; ++ i) {
		sc("%d%d%d", &a, &b, &c);
		e[a].push_back({b, c});
		e[b].push_back({a, c});
	}
	dfs(1, -1, 0);
	int u = 1;
	for (int i = 2; i <= n; ++ i) {
		if (dis[i] > dis[u]) u = i;
	}
	dfs(u, -1, 0);
	u = 1;
	for (int i = 2; i <= n; ++ i) {
		if (dis[i] > dis[u]) u = i;
	}
	u = dis[u];
	cout << u * 10 + (u + 1ll) * u / 2;
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/509606.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自学软件测试,从10K到40K的技术路线,也就是这些东西...

如果有一天我从梦中醒来时&#xff0c;发现自己的几年自动化测试工程师经验被抹掉&#xff0c;重新回到了一个小白的状态。我想要重新自学自动化测试&#xff0c;然后找到一份自己满意的测试工作&#xff0c;我想大概只需要6个月的时间就够了&#xff0c;如果比较顺利的话&…

Cursor--一款强大的国内智能AI语言模型(据说对接了chatgpt4)

简介 Cursor是一个集成了 GPT-4 的国内直接可以访问的&#xff0c;优秀而强大的免费代码生成器&#xff0c;可以帮助你快速编写、编辑和讨论代码。 它支持多种编程语言&#xff0c;如 Python, Java, C#, JavaScript 等&#xff0c;并且可以根据你的输入和需求自动生成代码片段…

eSIM证书要求-涉及规范SGP.22-SGP.26-1

文档 Official Document SGP.22 – RSP Technical Specification v2.4 Official Document SGP.26 - RSP Test Certificates Definition &#xff0c;包含官方测试证书 证书链的定义 三个曲线 为了便于互操作性&#xff0c;本规范仅限于以下三个曲线 AlgorithmIdentifier算法标…

英飞凌的AURIX TC4x系列芯片在汽车控制器上应用

英飞凌的AURIX TC4x系列微控制器--智能汽车安全的领导者来了! 汽车市场上出现了重大的颠覆者&#xff1a; 汽车E/E架构微控制器&#xff08;MCU&#xff09;在领域和区域控制方面的创新 加速从传统动力系统到xEV的转变 安全性和自动驾驶的提高。 英飞凌的AURIX TC4x系列解…

yolov5 8系列 labelme数据标注 并生成训练数据集

yolov5 8系列 labelme数据标注 数据集生成终极教程 一.数据集准备二.转换为yolo 数据集 一.数据集准备 创建一个data 文件夹在data文件夹下创建一个images 文件夹将所有图片数据放入images文件夹下 使用labelme标注数据 python环境下使用 pip install labelme 安装labelme在c…

边缘计算盒子要怎么选?

选择边缘计算盒子需要考虑以下几个因素&#xff1a; 性能需求&#xff1a;边缘计算盒子的性能应该与您的应用需求相匹配。如果需要处理大量数据或者部署多种复杂ai算法&#xff0c;那么就需要选择更高性能的盒子。 IVP06A边缘计算盒子是基于RV1126设计的一款支持深度智能边缘…

Makefile基础教程(条件判断语句)

文章目录 前言一、条件判断语句概念讲解1.ifeq 和 ifneq2.ifdef 和 ifndef3.ifeq 的比较操作符 二、条件判断语句的使用三、条件判断语句使用的注意事项四、条件判断语句只在预处理阶段有效总结 前言 本篇文章开始讲解Makefile中的条件判断语句&#xff0c;在各种编程语言中都…

远程桌面连接可以传文件么?

远程桌面连接是一种远程管理计算机的方式。它允许用户通过网络远程控制其他计算机。远程桌面连接可以用于各种目的&#xff0c;例如从远程地方访问办公室电脑、支持远程用户、教育、游戏等等。但是&#xff0c;在使用远程桌面连接时&#xff0c;用户可能会遇到一些问题&#xf…

8. 类的静态成员

一、对象的生产期 生存期&#xff1a;对象从诞生到结束的这段时间生存期分为静态生存期和动态生存期 1.1 静态生存期 对象的生存期与程序的运行期相同&#xff0c;则称它具有静态生存期在文件作用域中声明的对象都是具有静态生存期的若在函数内部的局部作用域中声明具有静态…

配置Windows终端像Linux终端一样直接执行Python脚本

配置Windows终端像Linux终端一样直接执行Python脚本 1. 将Python加入环境变量2. 将Python后缀加入环境变量PATHEXT中3. 修改Python脚本的默认打开方式4. *将Python脚本命令加入环境变量*5. 测试 在Linux系统中&#xff0c;在Python脚本的开头指定Python解释器路径&#xff0c;即…

Terry部落简介

通过一个简单的服务&#xff0c;整理使用的技术 服务简介 Terry部落&#xff0c;基于目前主流 Java Web 技术栈&#xff08;SpringBoot MyBatis-plus MySQL Redis Kafka Elasticsearch shiro …&#xff09;实现的个人博客服务。包含登陆、注册、文章发布、资源发布、鉴…

ACM - 数学 - 提高(还没学多少)

ACM - 数学 练习题 一、数论1、分解质因数 &#xff1a;AcWing 197. 阶乘分解2、求约数个数&#xff08;1&#xff09;AcWing 1294. 樱花 &#xff08;求 n&#xff01;约数个数之和&#xff09;&#xff08;2&#xff09;AcWing 198. 反素数 &#xff08;求 1 ~ N 中约数最多的…

human-NeRF 代码运行环境完全打包(根据照片视频生成三维模型)

包含模型和可以直接运行的数据的代码环境&#xff08;window环境&#xff0c;linux应该也可以运行&#xff09;&#xff0c;下载链接放在文章最后&#xff0c;不需要你自己再去下载任何的代码和模型了。 下载后输入三行命令在命令行界面即可将代码跑起来&#xff1a; pip insta…

C++14:AVL树

由于二叉搜索树在某些特定的情况下会退化成单叉树,为了解决这个问题&#xff0c;保证二叉搜索树能在绝大多数情况下保持高速搜索&#xff0c;G.M. Adelson-Velsky和E.M. Landis这两位俄国数学家提出了AVL树的概念&#xff0c;也就是高度平衡的搜索二叉树。 AVL树平衡大体逻辑&…

ACM - 数据结构 - 基础(数组模拟链表 / 栈 / 队列 + 字典树 + 并查集 + 堆 + 哈希)

数据结构 一、线性表1、单链表模板题&#xff1a;AcWing 826. 单链表 2、双链表模板题 AcWing 827. 双链表 3、栈数组模拟栈模板 AcWing 828. 模拟栈逆波兰简版模板例题1、逆波兰表达式&#xff1a;HDU 1237 简单计算器&#xff08;写得有点复杂&#xff09; 4、队列数组模拟队…

接口自动化测试可以使用的几个常用的框架

接口自动化测试可以使用以下几个常用的框架&#xff1a; 1、pytest pytest是一个用于Python编写单元测试和功能测试的框架。它提供了简洁的语法、灵活的扩展性和丰富的插件&#xff0c;可以帮助开发人员高效地编写测试用例&#xff0c;并快速定位和解决问题。 以下是pytest的…

数据结构刷题(三十一):1049. 最后一块石头的重量 II、完全背包理论、518零钱兑换II

一、1049. 最后一块石头的重量 II 1.思路&#xff1a;01背包问题&#xff0c;其中dp[j]表示容量为j的背包&#xff0c;最多可以背最大重量为dp[j]。 2.注意&#xff1a;递推公式dp[j] max(dp[j], dp[j - stones[i]] stones[i]);本题中的重量就是价值&#xff0c;所以第二个…

边缘计算盒子适合用于哪些场景?

边缘计算盒子适用于在智慧工地、智慧工厂、智慧园区和智慧城管等场景中可以实现多种算法功能&#xff0c;以下是一些应用和实现算法功能&#xff1a; 智慧工地&#xff1a;实时视频监控与分析&#xff1a;边缘计算盒子可以处理实时监控视频流&#xff0c;进行人员和车辆识别、…

OpenPCDet系列 | 5.PointPillars模型前向传播完整流程

文章目录 前向传播流程1. 模型初始化2. 模型训练前向传播前向传播流程 这里以PointPillars模型为例,在PointPillars模型中主要划分了以下4个主要的模块,以下某块首先会在build_network中进行初始化,然后在具体前向传播时通过forward函数进行。下面进行区分。 PointPillars…

新颖拓扑指纹助力虚拟筛选:ToDD革新计算机辅助药物发现之路

编译 | 于洲 今天我们介绍由Novartis集团的Novartis与德克萨斯大学达拉斯分校的Baris Coskunuzer为第一作者发表在NeurIPS 2022会议上的工作&#xff0c;文章介绍了一种新的虚拟筛选方法——ToDD模型&#xff0c;该方法使用了多参数持久性同调&#xff08;MP&#xff09;来生成…