#define _CRT_SECURE_NO_WARNINGS /* 算法:深度优先搜索(DFS) 时间:2021.10.28 目的:找出小哼解救小哈的最短路径 */ #include<stdio.h> int n, m, p, q, min = 99999999; int a[51][51], book[51][51]; voiddfs(int x, int y, intstep) { int next[4][2] = { {0,1},//向右走 {1,0},//向下走 {0,-1},//向左走 {-1,0}//向上走 }; int tx, ty, k; //判断是否到达小哈的位置 if (x == p && y == q) { //更新最小值 if (step < min) min = step; return;//请注意这里的返回很重要 }
//枚举4种走法 for (k = 0; k <= 3; k++) { //计算下一个点坐标 tx = x + next[k][0]; ty = y + next[k][1]; //判断是否越界 if (tx<1 || tx>n || ty<1 || ty>m) continue; //判断该点是否为障碍物或者已经在路径里 if (a[tx][ty] == 0 && book[tx][ty] == 0) { book[tx][ty] = 1;//标记这个点已经被走过 dfs(tx, ty, step + 1); book[tx][ty] = 0;//尝试结束,取消这个点的标记 } } return;
} intmain() { int i, j, startx, starty; //读入n和m,n为行m为列 scanf("%d %d", &n, &m); //读入迷宫 for (i = 1; i <= n; i++) for (j = 1; j <= m; j++) scanf("%d", &a[i][j]);
#define _CRT_SECURE_NO_WARNINGS /* 算法:广度优先搜索(BFS) 时间:2021.10.28 目的:找出小哼解救小哈的最短路径 */ #include<stdio.h> structnote { int x;//横坐标 int y;//纵坐标 int f;//父亲在队列中的编号,本题不要求输出路径,可以不需要f int s;//步数 }; intmain() { structnoteque[2501];//因为地图大小不超过50*50,因此队列扩展不会超过2500个
int a[51][51] = { 0 }, book[51][51] = { 0 }; //定义一个用于表示走的方向的数组 int next[4][2] = { {0,1},//向右走 {1,0},//向下走 {0,-1},//向左走 {-1,0}//向上走 }; int head, tail; int i, j, k, n, m, startx, starty, p, q, tx, ty, flag;
scanf("%d %d", &n, &m); for (i =1; i <= n; i++) for (j = 1; j <= m; j++) scanf("%d", &a[i][j]); scanf("%d %d %d %d", &startx, &starty, &p, &q);
#define _CRT_SECURE_NO_WARNINGS /* 算法:广度优先搜索(BFS) 时间:2021.10.28 目的:优化3.2炸弹人的代码 */ #include<stdio.h> structnode { int x;//横坐标 int y;//纵坐标 }; char a[20][21];//用来存储地图 intgetnum(int i, int j) { int sum, x, y; sum = 0;//sum用来计数(可以消灭的敌人数),所以初始化为0 //将坐标i,j复制到两个新变量x,y中,以便以后向上下左右四个方向统计可以消灭的敌人数
//向上统计可以消灭的敌人数 x = i; y = j; while (a[x][y] != '#')//判断的点不是墙,如果不是墙就继续 { //如果当前的点是敌人,则进行计数 if (a[x][y] == 'G') sum++; //x--的作用是继续向上统计 x--; }
//向下统计可以消灭的敌人数 x = i; y = j; while (a[x][y] != '#') { if (a[x][y] == 'G') sum++; //x++的作用是继续向下统计 x++; }
//向左统计可以消灭的敌人数 x = i; y = j; while (a[x][y] != '#') { if (a[x][y] == 'G') sum++; //y--的作用是继续向左统计 y--; }
//向右统计可以消灭的敌人数 x = i; y = j; while (a[x][y] != '#') { if (a[x][y] == 'G') sum++; //y++的作用是继续向右统计 y++; } return sum; } intmain() { structnodeque[401];//假设地图大小不超过20*20,因此队列扩展不会超过400个 int head, tail; int book[20][20] = { 0 };//定义一个标记数组并全部初始化为0 int i, j, k, sum, max = 0, mx, my, n, m, startx, starty, tx, ty;
#define _CRT_SECURE_NO_WARNINGS /* 算法:深度优先搜索(DFS) 时间:2021.10.29 目的:优化3.2炸弹人的代码 */ #include<stdio.h> char a[20][21]; int book[20][20], max, mx, my, n, m; intgetnum(int i, int j) { int sum, x, y; sum = 0;//sum用来计数(可以消灭的敌人数),所以初始化为0 //将坐标i,j复制到两个新变量x,y中,以便以后向上下左右四个方向统计可以消灭的敌人数
//向上统计可以消灭的敌人数 x = i; y = j; while (a[x][y] != '#')//判断的点不是墙,如果不是墙就继续 { //如果当前的点是敌人,则进行计数 if (a[x][y] == 'G') sum++; //x--的作用是继续向上统计 x--; }
//向下统计可以消灭的敌人数 x = i; y = j; while (a[x][y] != '#') { if (a[x][y] == 'G') sum++; //x++的作用是继续向下统计 x++; }
//向左统计可以消灭的敌人数 x = i; y = j; while (a[x][y] != '#') { if (a[x][y] == 'G') sum++; //y--的作用是继续向左统计 y--; }
//向右统计可以消灭的敌人数 x = i; y = j; while (a[x][y] != '#') { if (a[x][y] == 'G') sum++; //y++的作用是继续向上统计 y++; } return sum; } voiddfs(int x, int y) { //定义一个用于表示走的方向的数组 int next[4][2] = { {0,1},//向右走 {1,0},//向下走 {0,-1},//向左走 {-1,0}//向上走 }; int k, sum, tx, ty; //计算这个点当前可以消灭的敌人总数 sum = getnum(x, y);
//更新max的值 if (sum > max) { //如果当前的点统计出的所能消灭的敌人数大于max, //则更新max,并用mx和my记录当前点的坐标 max = sum; mx = x; my = y; } //枚举四个方向 for (k = 0; k <= 3; k++) { //下个点的坐标 tx = x + next[k][0]; ty = y + next[k][1]; //判断是否越界 if (tx<0 || tx>n - 1 || ty<0 || ty>m - 1) continue; //判断是否围墙或者已经走过 if (a[tx][ty] == '.' && book[tx][ty] == 0) { book[tx][ty] = 1;//标记这个点已走过 dfs(tx, ty);//开始尝试下一个点 //book[tx][ty]=0 //此处不用收回,与路径或者步数有关的深搜,需要收回标记,无关则不需要回收 } } return; } intmain() { int i, startx, starty; //读入n和m,n表示有多少行字符,m表示每行有多少字符 scanf("%d %d %d %d", &n, &m, &startx, &starty);
//读入n行字符 for (i = 0; i <= n - 1; i++) scanf("%s", a[i]); //从小人站的位置开始尝试 book[startx][starty] = 1; max = getnum(startx, starty); mx = startx; my = starty; dfs(startx, starty);
#define _CRT_SECURE_NO_WARNINGS // 算法:DFS // 时间:2021.10.29 #include<stdio.h> int a[51][51]; int book[51][51], n, m, sum; voiddfs(int x, int y) { //定义一个方向数组 int next[4][2] = { {0,1},//向右走 {1,0},//向下走 {0,-1},//向左走 {-1,0}//向上走 }; int k, tx, ty;
//枚举4个方向 for (k = 0; k <= 3; k++) { //计算下一步坐标 tx = x + next[k][0]; ty = y + next[k][1]; //判断是否越界 if (tx<1 || tx>n || ty<1 || ty>m) continue; //判断是否是陆地 if (a[tx][ty] > 0 && book[tx][ty] == 0) { sum++; book[tx][ty] = 1;//标记这个点已走过 dfs(tx, ty);//开始尝试下一个点 } } return; } intmain() { int i, j, startx, starty; scanf("%d %d %d %d", &n, &m, &startx, &starty); //读入地图 for (i = 1; i <= n; i++) for (j = 1; j <= n; j++) scanf("%d", &a[i][j]);
#define _CRT_SECURE_NO_WARNINGS // 时间:2021.10.30 // 目的:用广度优先搜索遍历图(无向) #include<stdio.h> intmain() { int i, j, n, m, a, b, cur, book[101] = { 0 }, e[101][101]; int que[10001], head, tail; scanf("%d %d", &n, &m); //初始化二维矩阵 for (i = 1; i <= n; i++) for (j = 1; j <= n; j++) if (i == j) e[i][j] = 0; else e[i][j] = 99999999;//假设99999999为无穷大
//读入顶点之间的边 for (i = 1; i <= m; i++) { scanf("%d %d", &a, &b); e[a][b] = 1; e[b][a] = 1;//因为这里是无向图 } //队列初始化 head = 1; tail = 1;
#define _CRT_SECURE_NO_WARNINGS // 时间:2021.11.3 #include<stdio.h> structnode { int x;//城市编号 int s;//转机次数 };
intmain() { structnodeque[2501]; int e[51][51] = { 0 }, book[51] = { 0 }; int head, tail; int i, j, n, m, a, b, cur, start, end, flag = 0; scanf("%d %d %d %d", &n, &m, &start, &end); //初始化二维矩阵 for (i = 1; i <= n; i++) for (j = 1; j <= n; j++) if (i == j) e[i][j] = 0; else e[i][j] = 99999999; //读入城市之间的航班 for (i = 1; i <= m; i++) { scanf("%d %d", &a, &b); //注意这里是无向图 e[a][b] = 1; e[b][a] = 1; } //队列初始化 head = 1; tail = 1;
#define _CRT_SECURE_NO_WARNINGS // 时间:2021.11.3 23点11分 // 算法:Dijkstra算法 // 目的:求解单源最短路径(指定一个点(源点)到其余各个顶点的最短路径) #include<stdio.h> intmain() { int e[10][10], dis[10], book[10], i, j, n, m, t1, t2, t3, u, v, min; int inf = 99999999;//用inf(infinity的缩写)存储一个我们认为的正无穷值 //读入n和m,n表示顶点个数,m表示边的条数 scanf("%d %d", &n, &m);
//初始化 for (i = 1; i <= n; i++) for (j = 1; j <= n; j++) if (i == j) e[i][j] = 0; else e[i][j] = inf; //读入边 for (i = 1; i <= m; i++) { scanf("%d %d %d", &t1, &t2, &t3); e[t1][t2] = t3; }
//初始化dis数组 for (i = 1; i <= n; i++) dis[i] = e[1][i];
//book数组初始化 for (i = 1; i <= n; i++) book[i] = 0; book[1] = 1; //Dijkstra算法核心语句 for (i = 1; i <= n - 1; i++)//一共n个顶点,1号本身已经在p集合里面了,所以 { //只需要循环n-1次即可 //找到离1号顶点最近的顶点 min = inf; for (j = 1; j <= n; j++) { if (book[j] == 0 && dis[j] < min) { min = dis[j]; u = j; } } book[u] = 1; for (v = 1; v <= n; v++) { if (e[u][v] < inf) { if (dis[v] > dis[u] + e[u][v]) dis[v] = dis[u] + e[u][v]; } } }
//输出最后的结果 for (i = 1; i <= n; i++) printf("%d ", dis[i]);
#define _CRT_SECURE_NO_WARNINGS // 时间:2021.11.14 22点08分 // 算法:Bellman-Ford // 目的:解决负权边得问题 #include<stdio.h> intmain() { int dis[10], bak[10], i, k, n, m, u[10], v[10], w[10], check, flag; int inf = 99999999;//用inf存储一个我们认为得正无穷值 //读入n和m,n表示顶点个数,m表示边的个数 scanf("%d %d", &n, &m);
//读入边 for (i = 1; i <= m; i++) scanf("%d %d %d", &u[i], &v[i], &w[i]);
//初始化dis数组,这里是1号顶点到其余各个顶点得初始路程 for (i = 1; i <= n; i++) dis[i] = inf; dis[1] = 0;
//Bellman-Ford算法核心语句 for (k = 1; k <= n - 1; k++) { //将dis数组备份到bak数组中 for (i = 1; i <= n; i++) bak[i] = dis[i]; //进行一轮松弛 for (i = 1; i <= m; i++) if (dis[v[i]] > dis[u[i]] + w[i]) dis[v[i]] = dis[u[i]] + w[i]; //松弛完毕后检测dis是否有更新 check = 0; for (i = 1; i <= n; i++) if (bak[i] != dis[i]) { check = 1; break; } if (check == 0) break;//如果dis数组没有更新,提前退出循环结束算法 } //检测负权回路 flag = 0; for (i = 1; i <= m; i++) if (dis[v[i]] > dis[u[i]] + w[i]) flag = 1;
if (flag == 1) printf("此图含有负权回路"); else { //输出最终的结果 for (i = 1; i <= n; i++) printf("%d ", dis[i]); } getchar(); getchar(); return0; }
#define _CRT_SECURE_NO_WARNINGS // 算法:堆排序(建立最小堆实现从小到大排序) // 时间:2021.11.15 16点02分 #include<stdio.h> int h[101];//用来存放堆的数组 int n;//用来存放堆中元素的个数,也就是堆的大小
//交换函数,用来交换队中两个元素的值 voidswap(int x, int y) { int t; t = h[x]; h[x] = h[y]; h[y] = t; }
//向下调整的函数 voidsiftdown(int i)//传入一个需要向下调整的结点编号i,即从堆的顶点开始向下调整 { int t, flag = 0;//flag用来标记是否需要继续向下调整 //当i结点有儿子(其实是至少有左儿子)并且有需要继续调整的时候循环就执行 while (i * 2 <= n && flag == 0) { //首先判断它和左儿子的关系,并用t记录值较小的结点编号 if (h[i] > h[i * 2]) t = i * 2; else t = i; //如果它有右儿子,再对右儿子进行讨论 if (i * 2 + 1 <= n) { //如果右儿子的值更小,更新较小的结点编号 if (h[t] > h[i * 2 + 1]) t = i * 2 + 1; } //如果发现最小的结点编号不是自己,说明子结点中有比父结点更小的 if (t != i) { swap(t, i);//交换它们 i = t; } else flag = 1;//否则说明当前的父结点已经比两个子结点都要小了,不需要在进行调整 } }
//建立堆的函数 voidcreat() { int i; //从最后一个非叶结点到第一个结点依次进行向上调整 for (i = n / 2; i >= 1; i--) { siftdown(i); } } //删除最大的元素 intdeletemax() { int t; t = h[1];//用一个临时变量记录堆顶点的值 h[1] = h[n];//将堆的最后一个点赋值到堆顶 n--;//堆的元素减少1 siftdown(1);//向下调整 return t;//返回之前记录的堆的顶点的最大值 }
intmain() { int i, num; //读入要排序的数字个数 scanf("%d", &num);
for (i = 1; i <= num; i++) scanf("%d", &h[i]); n = num;
//建堆 creat();
//删除顶部元素,连续删除n次,其实也就是从大到小把数输出出来 for (i = 1; i <= num; i++) printf("%d ", deletemax());
#define _CRT_SECURE_NO_WARNINGS // 算法:堆排序(建立最大堆实现从小到大排序) // 时间:2021.11.15 16点44分 #include<stdio.h> int h[101];//用来存放堆的数组 int n;//用来存储堆中元素的个数,也就是堆的大小
//交换函数,用来交换堆中的两个元素的值 voidswap(int x, int y) { int t; t = h[x]; h[x] = h[y]; h[y] = t; } //向下调整函数 voidsiftdown(int i)//传入一个需要向下调整的结点编号i,这里传入1,即从堆的顶点开始向下调整 { int t, flag = 0;//flag用来标记是否需要继续向下调整 //当结点有儿子(其实是至少有左儿子)并且有需要继续调整的时候循环就执行 while (i * 2 <= n && flag == 0) { //首先判断它和左儿子的关系,并且用t记录较大的结点编号 if (h[i] < h[i * 2]) t = i * 2; else t = i; //如果它有右儿子,再对右儿子进行讨论 if (i * 2 + 1 <= n) { //如果右儿子的值更大,更新较小的结点编号 if (h[t] < h[i * 2 + 1]) t = i * 2 + 1; } //如果发现最大的结点编号不是自己,说明子结点有比父结点更大的 if (t != i) { swap(t, i);//交换它们 i = t;//更新i为刚才与它交换的儿子结点的编号,便于接下来继续向下调整 } else flag = 1;//否则说明当前的父结点已经比两个子结点都要大了,不需要再进行调整了 } }
//建立堆的函数 voidcreat() { int i; //从最后一个非叶结点到第一个结点依次进行向上调整 for (i = n / 2; i >= 1; i--) { siftdown(i); } }
//堆排序 voidheapsort() { while (n > 1) { swap(1, n); n--; siftdown(1); } } intmain() { int i, num; //读入n个数 scanf("%d", &num);
for (i = 1; i <= num; i++) scanf("%d", &h[i]); n = num;
//建堆 creat();
//堆排序 heapsort();
//输出 for (i = 1; i <= num; i++) printf("%d ", h[i]);
#define _CRT_SECURE_NO_WARNINGS // 算法:并查集 // 时间:2021.11.15 19点30分 // 目的:找出一共多少个犯罪团伙,其实就是找出多少个“祖宗” #include<stdio.h> int f[1000] = { 0 }, n, m, k, sum = 0; //这里初始化,非常的重要,数组里面存的是自己数组下标的编号就好了 voidinit() { int i; for (i = 1; i <= n; i++) f[i] = i; } //这是找爹的递归函数,不停地去找爹,直到找到祖宗为止,其实就是去找犯罪团伙的最高领导人 //“擒贼先擒王”原则 intgetf(int v) { if (f[v] == v) return v; else { //这里是路径压缩,每次在函数返回的时候,顺带把路上遇到的人的“BOSS” //改为最后找到的祖宗编号,也就是犯罪团伙的最高领导人编号。这样可以提高 //今后找到犯罪团伙的最高领导人(其实是数的祖先)的速度 f[v] = getf(f[v]); return f[v]; } } //这里是合并两集合的函数 voidmerge(int v, int u) { int t1, t2; t1 = getf(v); t2 = getf(u); if (t1 != t2)//判断两个结点是否在一个集合中,即是否为一个祖先 { f[t2] = t1; //靠左原则,左边变成右边的BOSS,即把右边的集合,作为左边集合的子集合 //经过路径压缩以后,将f[u]的根的值也赋值为v的祖先f[t1] } }
intmain() { int i, x, y; scanf("%d %d", &n, &m); //初始化是必须的 init(); for (i = 1; i <= m; i++) { //开始合并犯罪团伙 scanf("%d %d", &x, &y); merge(x, y); } //最后扫描有多少个独立的犯罪团伙 for (i = 1; i <= n; i++) { if (f[i] == i) sum++; } printf("%d\n", sum); return0; }
#define _CRT_SECURE_NO_WARNINGS // 算法:图的最小生成树 // 时间:2021.11.15 20点23分 #include<stdio.h> structedge { int u; int v; int w; };//为了方便排序,这里创建了一个结构体用来存储边的关系 structedgee[10];//数组大小根据实际情况来设置,要比m的最大值大1 int n, m; int f[7] = { 0 }, sum = 0, count = 0;//并查集需要用到的一些变量 //f数组大小根据实际情况来设置,要比n的最大值大1 voidquicksort(int left, int right) { int i, j; structedget; if (left > right) return;
i = left; j = right; while (i != j) { //顺序很重要,要从右边开始找 while (e[j].w >= e[left].w && i < j) j--; //再从左边开始找 while (e[i].w <= e[left].w && i < j) i++; //交换 if (i < j) { t = e[i]; e[i] = e[j]; e[j] = t; } } //最终将基准数归位,将left和i互换 t = e[left]; e[left] = e[i]; e[i] = t;
quicksort(left, i - 1);//继续处理左边的,这里是一个递归的过程 quicksort(i+1, right);//继续处理右边的,这里是一个递归的过程 return; }
intmain() { int i; //读入n和m,n表示顶点个数,m表示边的条数 scanf("%d %d", &n, &m); //读入边,这里用一个结构体来存储边的关系 for (i = 1; i <= m; i++) scanf("%d %d %d", &e[i].u, &e[i].v, &e[i].w);
quicksort(1, m);//按照权值从小到大对边进行快速排序
//并查集初始化 for (i = 1; i <= n; i++) f[i] = i; //Kruskal算法核心部分 for (i = 1; i <= m; i++)//开始从小到大枚举每一条边 { //判断一条边的两个顶点是否已经联通,即判断是否在一个集合中 if (merge(e[i].u, e[i].v))//如果目前尚未不连通,则选用这条边 { count++; sum = sum + e[i].w; } if (count == n - 1)//直到选用了n-1条边之后退出循环 break; }