`void floyd()
{
int i, j, k; // i和j是起始和结束节点,k是中间节点
for (k = 1; k <= n; k++)
{
for (i = 1; i <= n; i++)
{
for (j = 1; j <= n; j++)
{
// 如果通过k节点的路径比当前i到j的路径短,那么更新g[i][j]
if (g[i][k] != INF && g[k][j] != INF && g[i][j] > g[i][k] + g[k][j])
{
g[i][j] = g[i][k] + g[k][j];
}
}
}
}
}`
首先要初始化图的邻接矩阵
`for (i = 1; i <= n; i++) // 初始化图的邻接矩阵
{
for (j = 1; j <= n; j++)
{
// 对角线上的元素为0,其他元素为无穷大
if (i == j)
{
g[i][j] = 0;
}
else
{
g[i][j] = INF;
}
}
}`
然后需要对重边进行选择
`for (i = 0; i < m; i++)
{
scanf("%d %d %d", &u, &v, &w);
if (g[u][v] > w) // 由于可能存在重边,我们需要保留权值最小的那条边
{
g[u][v] = w;
g[v][u] = w;
}
}`
最小生成树
模板
定义
`#include <stdlib.h>`
`typedef struct // 定义边的结构体
{
int u, v, w;
}Edge;
Edge edges[MAXM]; // 存储所有的边
int pa[MAXN]; // 并查集数组,用于判断两个节点是否在同一棵树中
int N, M; // N是节点数,M是边数
int cmp(Edge a, Edge b); // 边的比较函数,按照边的权重从小到大排序
int find(int x); // 并查集的查找函数,用于查找节点x的根节点
int kruskal(); // Kruskal算法函数
调用Kruskal算法
`int res = kruskal(); // 调用Kruskal算法计算最小生成树的权重
if (res == -1) // 如果图不连通
{
printf("orz\n");
}
else
{
printf("%d\n", res);
}`
边的比较函数
`int cmp(Edge a, Edge b) // 边的比较函数,按照边的权重从小到大排序
{
return (a.w - b.w);
}`
并查集的查找函数
`int find(int x) // 并查集的查找函数,用于查找节点x的根节点
{
while (pa[x] != x)
{
x = pa[x];
}
return x;
}`
Kruskal算法函数
`int kruskal() // Kruskal算法函数
{
int i, j;
for (i = 0; i < M; i++) // 将所有的边按照从小到大排序
{
for (j = i + 1; j < M; j++)
{
if (cmp(edges[i], edges[j]) > 0)
{
Edge temp = edges[i];
edges[i] = edges[j];
edges[j] = temp;
}
}
}
for (i = 1; i <= N; i++) // 初始化并查集
{
pa[i] = i;
}
int res = 0; // 存储最小生成树的权重
int cnt = 0; // 存储当前已经选择的边的数量
for (i = 0; i < M; i++) // 遍历所有的边
{
int u = find(edges[i].u); // 边的一个节点
int v = find(edges[i].v); // 边的另一个节点
// 如果两个节点不在同一棵树中,说明这条边可以添加到最小生成树中
if (u != v)
{
res += edges[i].w; // 更新最小生成树的权重
pa[u] = v; // 合并两棵树
cnt++; // 更新已经选择的边的数量
}
}
if (cnt != N - 1) // 如果选择边的数量小于N - 1,说明图不连通
{
return -1;
}
return res; // 返回最小生成树的权重
}
线性dp
模板
定义
`#include <stdlib.h>
int max(int a, int b); // 用于比较两个数的大小的函数`
`int dp[MAX][MAX]; // 定义一个二维数组,用于存储金字塔和动态规划的状态`
核心代码段
`for (i = r - 1; i >= 1; i--) // 从倒数第二行开始,逐行向上
{
for (j = 1; j <= i; j++)
{
// 选择下方或者右下方的较大值,然后加上当前位置的值
dp[i][j] += max(dp[i + 1][j], dp[i + 1][j + 1]);
}
}
# 搜索剪枝与记忆化搜索
模板
定义
`int max(int a, int b); // 求两个数的最大值
int dfs(int x, int y); // 深度优先搜索
#define MAX 10000
int R, C; // 区域的行数和列数
int height[MAX][MAX]; // 存储每个点的高度
int dp[MAX][MAX]; // 存储每个点的最长滑坡长度
int dx[4] = {-1, 1, 0, 0}; // 行
int dy[4] = {0, 0, -1, 1}; // 列`
动态规划
`or (i = 0; i < R; i++) // 对每个点进行深度优先搜索
{
for (j = 0; j < C; j++)
{
longest = max(longest, dfs(i, j)); // 更新最长滑坡长度
}
}`
深度优先搜索
`int dfs(int x, int y) // 深度优先搜索
{
int i, nx, ny;
if (dp[x][y] != 0) // 如果已经计算过该点的最长滑坡长度,则直接返回
{
return dp[x][y];
}
dp[x][y] = 1; // 初始化为1,表示至少有一个点(及自身)
for (i = 0; i < 4; i++) // 遍历四个方向
{
nx = x + dx[i];
ny = y + dy[i];
// 如果新的点在区域内,并且高度小于当前点的高度
if (nx >= 0 && nx < R && ny >= 0 && ny < C && height[nx][ny] < height[x][y])
{
dp[x][y] = max(dp[x][y], dfs(nx, ny) + 1); // 更新当前点的最长滑坡长度
}
}
return dp[x][y]; // 返回当前点的最长滑坡长度
}`
# 搜索
`void dfs(int x, int y) // 深度优先搜索函数
{
int i, nx, ny;
if (x == FX && y == FY) // 如果到达终点
{
ans++; // 找到一条路径,方案数加一
return; // 一旦找到目标,停止搜索,回溯到上一步,然后尝试其他可能的路径
}
for (i = 0; i < 4; i++) // 尝试四个方向的移动
{
nx = x + dx[i];
ny = y + dy[i];
// 判断新位置是非在迷宫内,且没有被访问过,且不是障碍
if (nx >= 1 && nx <= N && ny >= 1 && ny <= N && visited[nx][ny] == 0 && maze[nx][ny] == 0)
{
visited[nx][ny] = 1; // 标记为已访问
dfs(nx, ny); // 从新位置继续深度搜索
visited[nx][ny] = 0; // 搜索完后,标记为未访问
}
}
}
广度优先搜索
模板
定义
`void bfs(int n, int m, int x, int y);`
`int dx[8] = {-2, -1, 1, 2, 2, 1, -1, -2}; // 马可以移动的8 个方向
int dy[8] = {1, 2, 2, 1, -1, -2, -2, -1};`
`int queue[MAX * MAX][2]; // 定义队列,用于存储待访问的位置`
核心代码段
`void bfs(int n, int m, int x, int y)
{
int i, j;
for (i = 0; i < n; i++) // 初始化棋盘,所有位置的步数都设为-1
{
for (j = 0; j < m; j++)
{
board[i][j] = -1;
}
}
board[x][y] = 0; // 马的初始位置的步数设为0
int front = 0, rear = 0; // // 创建队列,并将马的初始化位置加入队列
queue[rear][0] = x; // 新元素总是被添加到到尾部
queue[rear++][1] = y; // 是储存到同一个位置,并且rear++(后置递增操作)使得尾部位置向后移动一位,为下一个元素的添加做好准备
while (front != rear) // 当队列不为空时(头部位置不等于尾部位置),进入循环
{
x = queue[front][0]; // 取出队列的第一个元素
y = queue[front++][1]; // 并将头部位置向后移动一位,为下一个元素的处理做好准备
for (i = 0; i < 8; i++) // 遍历马可以移动的所有位置
{
int nx = x + dx[i], ny = y + dy[i];
// 检查新位置是否在棋盘内,并且是否已经被访问过
if (nx >= 0 && nx < n && ny >= 0 && ny < m && board[nx][ny] == -1)
{
queue[rear][0] = nx; // 将新位置加入队列,并更新步数
queue[rear++][1] = ny;
board[nx][ny] = board[x][y] + 1;
}
}
}
}
# 数论基础
快速幂
模板
定义
`#include <stdlib.h>`
核心代码段
`long long a, b, p, ans,x , y;
scanf("%lld %lld %lld", &a, &b, &p);
x = a;
y = b;
ans = 1;
x = x % p;
while (y > 0)
{
if ((y % 2) == 1)
{
ans = (ans * x) % p;
}
y = y / 2;
x = (x * x) % p;
}
printf("%lld^%lld mod %lld=%lld", a, b, p, ans);