常用算法代码模板 (3) :搜索与图论

news/2024/5/14 19:02:49

AcWing算法基础课笔记与常用算法模板 (3) ——搜索与图论

常用算法代码模板 (1) :基础算法
常用算法代码模板 (2) :数据结构
常用算法代码模板 (3) :搜索与图论
常用算法代码模板 (4) :数学知识

文章目录

  • 0 搜索技巧
  • 1 树与图的存储
    • 1.1 邻接矩阵
    • 1.2 邻接表
  • 2 树与图的遍历
    • 2.1 深度优先遍历
    • 2.2 宽度优先遍历
  • 3 拓扑排序
  • 4 最短路径
    • 4.1 朴素Dijkstra算法
    • 4.2 堆优化Dijkstra算法
    • 4.3 Bellman-Ford算法
    • 4.4 SPFA算法
    • 4.5 Floyd算法
  • 5 最小生成树
    • 5.1 朴素版Prim算法
    • 5.2 Kruskal算法
  • 6 二分图
    • 6.1 染色法
    • 6.2 匈牙利算法


0 搜索技巧

  • DFS
    • 分析问题:画图归纳
    • 保存现场
    • 剪枝
  • BFS
    • 必要时开距离数组记录 <u>第一次 </u>到达每个点时的距离(即为到达每个点的最短路距离)

偏移量

控制点在二维平面上移动的方向(搜索方向),可设定方向下标按顺时针()递增。此时对于方向下标 i,其反方向下标为 i ^ 2(对2做按位异或运算),也可手动if设置求得。

int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};	// 上(-1, 0),右(0, 1),下(1, 0),左(0, -1)

1 树与图的存储

1.1 邻接矩阵

注意无法存储重边

int g[N][N];	// g[a][b]存储有向边<a, b>/* 初始化 */
memset(g, 0x3f, sizeof g);

1.2 邻接表

const int N = 1e5, M = 2 * N;int n, m;	// 点数、边数
int h[N], e[M], ne[M], idx;	// h[k]为点k的边表的头指针/* 初始化 */
memset(h, -1, sizeof h);
idx = 0;/* 添加一条边<a, b> */
void add(int a, int b) {e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

2 树与图的遍历

时间复杂度: O ( n + m ) O(n+m) O(n+m)

2.1 深度优先遍历

bool st[N];int dfs(int u) {st[u] = true;for (int i = h[u]; ~i; i = ne[i]) {	// 遍历u的所有出边int v = e[i];if (!st[v]) dfs(v);}
}

2.2 宽度优先遍历

bool st[N];	// V: [1 ... n]void bfs() {queue<int> q;q.push(1);	// 队中压入初值st[1] = true;while (!q.empty()) {int t = q.front();q.pop();for (int i = h[t]; ~i; i = ne[i]) {	// 遍历点t的所有出边int u = e[i];if (!st[u]) {st[u] = true;q.push(u);}}}
}

3 拓扑排序

时间复杂度: O ( n + m ) O(n+m) O(n+m)

int n;		// V: [1 ... n]
int q[N], hh = 0, tt = -1;	// 顶点队列,存储拓扑序列
int d[N];	// d[i]存储点i的入度/* 拓扑排序:将拓扑序列存在队列中 */
bool topsort() {for (int i = 1; i <= n; i++)	// 将所有度为0的点入队if (d[i] == 0) q[++tt] = i;while (hh <= tt) {int t = q[hh++];for (int i = h[t]; ~i; i = ne[i]) {	// 遍历点t的所有出边int u = e[i];	// 该出边对应的点uif (--d[u] == 0) q[++tt] = u;	// 删去该出边并判定:u入度变为0了则入队}}return tt = n - 1;	// 如果所有点都入队了,说明存在拓扑序列;否则不存在拓扑序列
}/* 输出拓扑序列(若存在) */
if (topsort()) {for (int i = 0; i < n; i++) printf("%d ", q[i]);puts("");
}

4 最短路径

4.1 朴素Dijkstra算法

时间复杂度: O ( n 2 + m ) O(n^2+m) O(n2+m)

适用情形:稠密图

int n;			// V: [1 ... n]
int g[N][N];	// 邻接矩阵图(带权)
int dist[N];	// dist[]存储起点到每个点的最短路径
bool st[N];		// st[]标记每个点的最短路是否已被确定/* 求起点S到终点T的最短路,若不存在则返回-1 */
int dijkstra(int S, int T) {memset(dist, 0x3f, sizeof dist);dist[S] = 0;	// 这里只先设起点的distfor (int i = 0; i < n; i++) {	// 迭代n次(第1轮预处理起点)int t = -1;	// 在还未确定最短路的点中,寻找最短距离点tfor (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++)	// 用t更新其他点的距离if (dist[j] > dist[t] + g[t][j])dist[j] = dist[t] + g[t][j];}if (dist[T] == 0x3f3f3f3f) return -1;return dist[T];
}

4.2 堆优化Dijkstra算法

时间复杂度: O ( m log ⁡ n ) O(m\log n) O(mlogn)

适用情形:稀疏图

typedef pair<int, int> PII;int n;			// V: [1 ... n]
int h[N], e[M], w[M], ne[M], idx;	// 邻接表图,w[i]存边i的权值
int dist[N];	// dist[]存储起点到每个点的最短路径
bool st[N];		// st[]标记每个点的最短路是否已被确定/* 求起点S到终点T的最短路,若不存在则返回-1 */
int dijkstra(int S, int T) {memset(dist, 0x3f, sizeof dist);dist[S] = 0;	// 这里也只先设起点的distpriority_queue<PII, vector<PII>, greater<PII> > heap;	// 堆(优先队列)heap.push({0, S});	// <distance, vertex>while (!heap.empty()) {auto t = heap.top();heap.pop();int u = t.second, distance = t.first;	// 用堆得到最近的点及与其距离if (st[u]) continue;	// 若已确定则跳过st[u] = true;for (int i = h[u]; ~i; i = ne[i]) {int v = e[i];if (dist[v] > distance + w[i]) {dist[v] = distance + w[i];heap.push({dist[v], v});}}}if (dist[T] == 0x3f3f3f3f) return -1;return dist[T];
}

4.3 Bellman-Ford算法

时间复杂度: O ( n m ) O(nm) O(nm)

适用情形:存在负权边的图

如果第 n n n 次迭代仍然会松弛三角不等式,就说明存在一条长度是 n + 1 n+1 n+1 的最短路径,由抽屉原理,路径中至少存在两个相同的点,说明图中存在负权回路(负环)。

int n, m;		// V: [1 ... n]
struct Edge {int a, b, w;
} edges[M];		// 边集,存储权值为w的有向边<a, b>
int dist[N];	// dist[]存储起点到每个点的最短路径/* 求起点S到终点T的最短路,若不存在则返回-1 */
int bellman_ford(int S, int T) {memset(dist, 0x3f, sizeof dist);dist[S] = 0;for (int i = 0; i < n; i++) {	// 要求最大长度为n的最短路径,故迭代n次for (int j = 0; j < m; j++) {	// 每次遍历全部m条边int a = edges[j].a, b = edges[j].b, w = edges[j].w;if (dist[b] > dist[a] + w)	// 松弛操作:更新当前distdist[b] = dist[a] + w;}}if (dist[T] > 0x3f3f3f3f / 2) return 0x3f3f3f3f;	// 因为负权边的存在,可能略低于INFreturn dist[T];
}

应用:求有边数限制的最短路

限制 k k k 条边就进行 k k k 轮迭代遍历,遍历开始前需先备份 dist[]backup[],用其将 dist[]更新。

int n, m, k;	// 限制最短路最多经过k条边
struct Edge {int a, b, w;
} edges[M];
int dist[N], backup[N];	// backup[]备份dist[]数组,防止发生串联(用改后数据去改别人)/* 求起点S到终点T的最短路,若不存在则返回-1 */
int bellman_ford(int S, int T) {memset(dist, 0x3f, sizeof dist);dist[S] = 0;for (int i = 0; i < k; i++) {	// 限制k条边,则迭代k次memcpy(backup, dist, sizeof dist);	// 遍历边前先将dist拷贝至备份数组for (int j = 0; j < m; j++) {int a = edges[j].a, b = edges[j].b, w = edges[j].w;if (dist[b] > backup[a] + w)	// 使用备份数组做松弛操作dist[b] = backup[a] + w;}}if (dist[T] > 0x3f3f3f3f / 2) return 0x3f3f3f3f;return dist[T];
}

4.4 SPFA算法

时间复杂度:平均 O ( m ) O(m) O(m) ,最坏 O ( n m ) O(nm) O(nm)

队列优化的Bellman-Ford算法:后继变小了当前dist才变小

int n;			// V: [1 ... n]
int h[N], e[M], w[M], ne[M], idx;	// 邻接表图,w[i]存边i的权值
int dist[N];	// dist[]存储起点到每个点的最短路径
bool st[N];		// st[]标记每个点的最短路是否已被确定int spfa(int S, int T) {memset(dist, 0x3f, sizeof dist);dist[S] = 0;queue<int> q;	// 队中存放待更新的点(用堆也行)q.push(S);st[S] = true;	// 结点入队时做标记while (!q.empty()) {	// 使用BFS的思想auto t = q.front();q.pop();st[t] = false;	// 结点出队时撤销标记(之后可能需再次入队被更新)for (int i = h[t]; ~i; i = ne[i]) {int u = e[i];if (dist[u] > dist[t] + w[i]) {dist[u] = dist[t] + w[i];if (!st[u]) {	// 若更新了u的距离,则其出边所指也可能待更新,判断将其入队q.push(u);st[u] = true;}}}}if (dist[T] == 0x3f3f3f3f) return 0x3f3f3f3f;return dist[T];
}

应用:SPFA判断图中是否存在负环

时间复杂度: O ( n m ) O(nm) O(nm)

不需要初始化 dist[],因此之后正权入边顶点永不会被更新;并且为消除某点可能无法到达负环的影响,将所有点全入队并标记!

原理:若某条最短路径上有 n n n 个点(除了自己),则加上自己之后一共有 n + 1 n+1 n+1 个点,由抽屉原理一定有两个点相同,所以存在负环。

int n;
int h[N], e[M], w[M], ne[M], idx;]
int dist[N], cnt[N];	// cnt[x]存储起点(任意)到x的最短路中经过的点数
bool st[N];/* 如果存在负环,则返回true,否则返回false */
bool spfa() {queue<int> q;	// 不需要初始化dist数组,直接将所有点全入队并标记!for (int i = 1; i <= n; i++) {q.push(i);st[i] = true;}while (!q.empty()) {auto t = q.front();q.pop();st[t] = false;for (int i = h[t]; ~i; i = ne[i]) {int u = e[i];if (dist[u] > dist[t] + w[i]) {dist[u] = dist[t] + w[i];cnt[u] = cnt[t] + 1;	// 若更新了u的距离,则立即更新其cnt(前驱加1)if (cnt[u] >= n) return true;	// 若最短路已包含至少n个点(不含自身),则有负环if (!st[u]) {q.push(u);st[u] = true;}}}}return false;	// 跳出循环则说明无负环
}

4.5 Floyd算法

时间复杂度: O ( n 3 ) O(n^3) O(n3)

基于动态规划

int n;			// V: [1 ... n]
int d[N][N];	// 邻接矩阵图,经过Floyd()操作后变为存储最短距离/* 初始化 */
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;/* 算法结束后,d[a][b]表示a到b的最短距离 */
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][j], d[i][k] + d[k][j]);
}

5 最小生成树

5.1 朴素版Prim算法

时间复杂度: O ( n 2 + m ) O(n^2+m) O(n2+m)

必须先累加 res再更新 dist[],以避免负自环污染当前 t最短距离

const int INF = 0x3f3f3f3f;int n;			// V: [1 ... n]
int g[N][N];	// 邻接矩阵图
int dist[N];	// dist[]存储起点到当前最小生成树(MST)的最短距离
bool st[N];		// st[]标记每个点是否已经在生成树中/* 若图不连通,则返回INF,否则返回最小生成树的最小代价 */
int prim() {memset(dist, 0x3f, sizeof dist);	// 仅计算最小代价,故无需另设起点int res = 0;	// 存储最小代价for (int i = 0; i < n; i++) {	// 迭代n次(第1轮预处理生成树根)int t = -1;	// 在还未并入MST的点中,寻找最短距离点tfor (int j = 1; j <= n; j++)if (!st[j] && (t == -1 || dist[t] > dist[j]))t = j;if (i && dist[t] == INF) return INF;	// 从第2轮起,若最短距离为无穷,则说明不连通if (i) res += dist[t];	// 从第2轮起,将t的距离计入最小代价(须先累加res)st[t] = true;	// 将t并入MSTfor (int j = 1; j <= n; j++)	// 用新并入的t更新各点到生成树的距离if (dist[j] > g[t][j]) 	// 与dij不同,不应加前驱的dist(求取到整棵树的距离)dist[j] = g[t][j];}return res;
}

5.2 Kruskal算法

时间复杂度: O ( m log ⁡ m ) O(m\log m) O(mlogm)

int n, m;		// V: [1 ... n]
struct Edge {int a, b, w;bool operator<(const Edge &t) const {	// 重载运算符,用于按权递增排序return w < t.w;}
} edges[MAXM];	// 边集,存储权值为w的有向边<a, b>
int p[N];		// 并查集/* 并查集核心操作 */
int find(int x) {if (p[x] != x) p[x] = find(p[x]);return p[x];
}/* 若图不连通,则返回INF,否则返回最小生成树的最小代价 */
int kruskal() {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++) {	// 枚举所有边,将合适的边并入MST(加入集合)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) return INF;	// 判定连通性(连通的必要条件:|E| = |V| - 1)return res;
}

6 二分图

在这里插入图片描述

定义:二分图可将图中顶点划分两个集合,使得集合内顶点互不邻接,不同集合顶点可邻接

定理:图为二分图 ⇔ \Leftrightarrow 图中不含奇数环

6.1 染色法

时间复杂度: O ( n + m ) O(n+m) O(n+m)

判断是否是二分图

思想:若为二分图,则与黑点相连的点均为白色,与白点相连的点均为黑色(邻接顶点不得同色)

int n;			// V: [1 ... n]
int h[N], e[M], ne[M], idx;	// 邻接表图
int color[N];	// 每个点的颜色:-1未染色,0白色,1黑色/* 用dfs给结点u染颜色c,一切顺利返回true,出现冲突则返回false */
bool dfs(int u, int c) {color[u] = c;	// 给结点u染颜色cfor (int i = h[u]; ~i; i = ne[i]) {	// 遍历所有从结点u指出的点int v = e[i];if (color[v] == -1) {	// 若v未染色则将其染与u相反的色(!c)并判定是否冲突if (!dfs(v, !c)) return false;} else if (color[v] == c) return false;	// 若v与u同色则出现冲突}return true;
}/* 用染色法判断图是否是二分图 */
memset(color, -1, sizeof color);
bool success = true;
for (int i = 1; i <= n; i++)	// 遍历所有顶点,若未染色则染白色并判定是否冲突if (color[i] == -1)if (!dfs(i, 0)) {success = false;break;}

6.2 匈牙利算法

时间复杂度:最差 O ( n m ) O(nm) O(nm) ,实际运行时间一般远小于 O ( n m ) O(nm) O(nm)

用于求二分图的最大匹配数(匹配:某两个点有且只有他们之间有边,与别人无边)

匈牙利算法中只会用到从第1个集合指向第2个集合的边,所以这里只用存一个方向的边。

先欣赏y神讲解再看代码

int n1, n2;		// 二分图中两个集合的点数。集合1: [1 ... n1]、集合2: [1 ... n2]
int h[N], e[M], ne[M], idx;	// 邻接表图,只存集合1到集合2的边
int match[N];	// match[i] = j表示集合2的点i当前匹配集合1的点j(j=0表示暂无匹配)
bool st[N];		// st[i]标记集合2的点i是否已经被遍历过/* 寻找与集合1的点u匹配集合2的点,返回是否成功 */
bool find(int u) {for (int i = h[u]; ~i; i = ne[i]) {	// "遍历所有可能的她"int v = e[i];if (!st[v]) {st[v] = true;if (match[v] == 0 || find(match[v])) {	// 判定"若她有则'去找他'"match[v] = u;	// 与她匹配return true;}}}return false;
}/* 求最大匹配数 */
int res = 0;
for (int i = 1; i <= n1; i++) {	// 依次枚举集合1的每个点去匹配集合2的点memset(st, false, sizeof st);	// 每次重置遍历标记if (find(i)) res++;
}

http://www.mrgr.cn/p/37683467

相关文章

OpenHarmony 项目实战:智能体重秤

一、简介 本 demo 基于 OpenHarmony3.1Beta 版本开发&#xff0c;该样例能够接入数字管家应用&#xff0c;通过数字管家应用监测体重秤上报数据&#xff0c;获得当前测量到的体重&#xff0c;身高&#xff0c;并在应用端形成一段时间内记录的体重值&#xff0c;以折线图的形式…

在 UOS 统信安装 dotnet sdk 失败 提示 failed the verification

在 UOS 统信安装 dotnet sdk 失败 提示 You cannot install /home/lindexi/packages-microsoft-prod.deb that failed the verification, please go to Security Center - Security Tools - Application Security to adjust这是群里的伙伴报告的问题,从错误提示看需要在安全工…

制作一个能构建 dotnet AOT 的 gitlab ruuner 的 Debian docker 镜像

我的需求是需要有一个能够构建出 dotnet 的 AOT 包的环境,要求这个环境能解决 glibc 兼容依赖的问题,能打出来 x64 和 arm64 的 AOT 的包,且能够运行 gitlab runner 对接自动构建需求 以下是我列举的需求支持制作能在 UOS 系统和麒麟系统上运行的包 支持制作出来的包是 AOT …

个人学习总结__打开摄像头、播放网络视频的以及ffmpeg推流

前言 最近入手了一款非常便宜的usb摄像头&#xff08;买回来感觉画质很低&#xff0c;没有描述的4k&#xff0c;不过也够用于学习了&#xff09;,想着利用它来开启流媒体相关技术的学习。第一步便是打开摄像头&#xff0c;从而才能够对它进行一系列后续操作&#xff0c;诸如实…

将要上市的自动驾驶新书《自动驾驶系统开发》中摘录片段

全书共分15章&#xff1a;第1章是自动驾驶系统的概述&#xff08;场景分类、开发路径和数据闭环等&#xff09;&#xff0c;第2章简介自动驾驶的基础理论&#xff0c;即计算机视觉和深度学习等&#xff0c;第3&#xff5e;4章是自动驾驶的软硬件平台分析&#xff0c;包括传感器…

【PyTorch与深度学习】2、PyTorch张量的运算API(上)

课程地址 最近做实验发现自己还是基础框架上掌握得不好&#xff0c;于是开始重学一遍PyTorch框架&#xff0c;这个是课程笔记&#xff0c;这个课还是讲的简略&#xff0c;我半小时的课听了一个半小时。 1. 张量 1.1 张量操作 &#xff08;1&#xff09;chunk&#xff1a;将一…

Web前端开发之CSS_2

关系选择器CSS盒子模型弹性盒子模型文档流浮动清除浮动定位 1. 关系选择器 1.1 后代选择器 E F{} 选择所有被 E 元素包含的 F 元素&#xff0c;中间用空格隔开 <ul> <li>后代列表1</li> <div> <ol> <li>后代列表2</li> </ol>…

Vue从入门到精通-01-Vue的介绍和vue-cli

MVVM模式 Model&#xff1a;负责数据存储 View&#xff1a;负责页面展示 View Model&#xff1a;负责业务逻辑处理&#xff08;比如Ajax请求等&#xff09;&#xff0c;对数据进行加工后交给视图展示 关于框架 为什么要学习流行框架 1、企业为了提高开发效率&#xff1a;…

linux的压缩与备份

一、打包 格式&#xff1a;tar -参数 <打包文件名> <打包的目标> 作用&#xff1a;将文件或者目录打包 重要参数&#xff1a;-f 使用归档文件&#xff0c;一定要加上这个参数 -c 新建打包文件 -x 解包文件 -t 可以不用解包就能查看包文件内容 -v 打包和解包时显…

读天才与算法:人脑与AI的数学思维笔记13_Coq证明助手

读天才与算法:人脑与AI的数学思维笔记13_Coq证明助手1. 计算机 1.1. 对于计算机来说,它就很擅长处理这种重复而机械且计算量庞大的任务 1.1.1. 在速度与准确性等方面,计算机是远超过手工计算的 1.2. 计算机只能执行指令,并无自主创造力 1.…

Android --- 常见UI组件

TextView 文本视图 设置字体大小&#xff1a;android:textSize"20sp" 用sp 设置颜色&#xff1a;android:textColor"#00ffff" 设置倍距(行距)&#xff1a;android:lineSpacingMultiplier"2" 设置具体行距&#xff1a;android:lineSpacingExtra&q…

学习STM32第二十天

低功耗编程 一、修改主频 STM32F4xx系列主频为168MHz&#xff0c;当板载8MHz晶振时&#xff0c;系统时钟HCLK满足公式 H C L K H S E P L L N P L L M P L L P HCLK \frac{HSE \times PLLN}{PLLM \times PLLP} HCLKPLLMPLLPHSEPLLN​&#xff0c;在文件stm32f4xx.h中可修…

Django初步了解

目录 一、什么是Django 二、Django的设计模式 三、涉及的英文缩写及其含义 四、安装&#xff08;官方教程&#xff09; 一、什么是Django Django是一个Python Web框架&#xff0c;可以快速开发网站&#xff0c;提供一站式的解决方案&#xff0c;包括缓存、数据库ORM、后台…

甘特图是什么?利用甘特图来优化项目管理流程

在现代项目管理中,图表是一种强大而直观的工具,可以帮助项目经理和团队成员清晰地了解并掌控整个项目进程。其中,甘特图是最常用和最有效的图表之一。 甘特图是一种条形图,可以用来直观地展示项目中各个任务的进度、持续时间和相互关系。它由一个横轴和一个纵轴组成。横轴代表时…

STM32入门_江协科技_1~2_OB记录的自学笔记_STM32简介

1.综述 1.1. 课程简介 手打代码是加入了实操&#xff0c;增加学习效果&#xff1b; STM最小系统板面包板的硬件平台&#xff1b; 配套0.96寸的显示屏&#xff0c;便于调试&#xff1b; 因为使用面板板&#xff0c;所以如果程序现象不出来也有可能是硬件连接的问题&#xff1b; …

YOLOv8改进项目汇总-超全改进-ultralyticsPro介绍:订阅了《芒果YOLOv8原创改进专栏》的读者免费赠送,包括很多稀有改进

&#x1f525;&#x1f525;&#x1f525;专注于YOLOv8改进&#xff0c;NEW - YOLOv8 &#x1f680; in PyTorch >, Support to improve Backbone, Neck, Head, Loss, IoU, LA, NMS and other modules&#x1f680; Makes YOLOv8 improvements easy again 芒果出品 YOLOv8…

[转帖]oracle SGA详解

https://www.cnblogs.com/li-mei/p/5015788.html SGA(System Global Area)系统全局区。这是一个非常庞大的内存区间,也是为什么开启oracle之后占用了很大内存的原因。SGA分为不同的池,我们可以通过视图v$sgastat查看,如下所示。 SQL> select pool ,sum(bytes) bytes …

Redis的面试

认识Redis 认识NoSQL SQL&#xff08;关系型数据库&#xff09; NoSQL&#xff08;非关系型数据库&#xff09; 1.结构化 非结构化 2.关联的 非关联的 3.SQL查询 非SQL 4.事务 …

【氮化镓】GaN 器件的高温运行

《High Temperature Operation of E-Mode and D-Mode AlGaN/GaN MIS-HEMTs With Recessed Gates》&#xff0c;由HANWOOL LEE, HOJOON RYU, JUNZHE KANG, 和 WENJUAN ZHU (IEEE高级会员) 四位作者共同撰写&#xff0c;发表在《IEEE Journal of the Electron Devices Society》上…

微软如何打造数字零售力航母系列科普03 - Mendix是谁?作为致力于企业低代码服务平台的领头羊,它解决了哪些问题?

一、Mendix 成立的背景 Mendix的成立是为了解决软件开发中最大的问题&#xff1a;业务和IT之间的脱节。这一挑战在各个行业和地区都很普遍&#xff0c;很简单&#xff1a;业务需求通常被描述为IT无法正确解释并转化为软件。业务和IT之间缺乏协作的原因是传统的代码将开发过程限…