DFS
全排列
objectivec
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#define N 10
bool book[N];
int n;
int a[N];
int tmp[N];int size = 0;
void dfs(){
if(size == n){
for(int i = 1; i <= size; i++){
printf("%d ", tmp[i]);
}
printf("\n");
return;
}
for(int i = 1; i <= n; i++){
if(!book[i]){
tmp[++size] = i;
book[i] = true;
dfs();
//回溯
book[i] = false;
size--;
}
}
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++){
book[i] = false;
a[i] = i;
}
dfs();
return 0;
}
组合数
排列数和组合数的差别在于:
- 排列数:每次递归的循环都从头开始,需要一个book数组记录已经被访问过的
- 组合数:每次递归都有一个开始的位置,不可能返回去找前面的数,因此不需要book数组记录
objectivec
/**
* Return an array of arrays of size *returnSize.
* The sizes of the arrays are returned as *returnColumnSizes array.
* Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
*/
#define N 25
int tmp[N];
int size = 0;
void dfs(int n, int k, int start, int** res, int* returnSize){
if(size == k){
//找到一种
res[(*returnSize)] = malloc((size + 1) * sizeof(int));
for(int i = 0; i < size; i++){
res[(*returnSize)][i] = tmp[i];
}
(*returnSize)++;
return;
}
for(int i = start; i <= n; i++){
tmp[size++] = i;
dfs(n, k, i + 1, res, returnSize);
size--;
}
}
int** combine(int n, int k, int* returnSize, int** returnColumnSizes) {
//有返回结果,先定义
int** res = malloc(200000 * sizeof(int*));
*returnSize = 0;
(*returnColumnSizes) = malloc(200000 * sizeof(int));
dfs(n, k, 1, res, returnSize);
for(int i = 0; i < (*returnSize); i++){
(*returnColumnSizes)[i] = k;
}
return res;
}
BFS
走迷宫
objectivec
#include<stdio.h>
#include<stdbool.h>
#define N 105
//广搜需要标记数组
bool book[N][N];
int graph[N][N];//邻接矩阵存储图
int n, m;
//存储每一个的最短距离
int res[N][N];
//广搜需要队列,二维数组模拟
// int q[N][2];//注意数组范围是否可以装的下
int q[N * N][2];
int front = 0; int rear = -1;
//广搜向四周散开
int dir[4][2] = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}};//右下上左
void bfs(int x, int y){
//进来先进队列
q[++rear][0] = x;
q[rear][1] = y;
//进了队列就标记
book[x][y] = true;
while(front <= rear){
//队列不为空,就每遍历完
//先取出第一个,然后四周散开
int curx = q[front][0];
int cury = q[front++][1];//取点出队同时进行
for(int i = 0; i < 4; i++){
int nextx = curx + dir[i][0];
int nexty = cury + dir[i][1];
//判断是否越界
if(nexty < 1 || nextx < 1 || nextx > n || nexty > m){
continue;//这个点跳过
}
//如果可走且未走过,入队
if(!book[nextx][nexty] && !graph[nextx][nexty]){
q[++rear][0] = nextx;
q[rear][1] = nexty;
//进了队列就标记
book[nextx][nexty] = true;
//实时更新距离
res[nextx][nexty] = res[curx][cury] + 1;
}
}
}
}
int main(){
scanf("%d %d", &n, &m);
int x;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
scanf("%d", &x);
graph[i][j] = x;
book[i][j] = false;
}
}
bfs(1, 1);
printf("%d", res[n][m]);
return 0;
}
岛屿数量
objectivec
#define N 305
//广搜标记
int book[N][N];
int q[N * N][2];
int front = 0; int rear = -1;
//四面八方
int dir[4][2] = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
void bfs(int x, int y, int n, int m, char** grid){
//进来先入队,打上标记
q[++rear][0] = x;
q[rear][1] = y;
book[x][y] = true;
while(front <= rear){
//队列不为空,先取出来
int curx = q[front][0];
int cury = q[front++][1];
for(int i = 0; i < 4; i++){
int nextx = curx + dir[i][0];
int nexty = cury + dir[i][1];
if(nextx < 0 || nexty < 0 || nextx >= n || nexty >= m){
continue;
}
//没找过,且未陆地,说明是同一块
if(book[nextx][nexty] == 0 && grid[nextx][nexty] == '1'){
q[++rear][0] = nextx;
q[rear][1] = nexty;
// printf("%d %d\n", nextx, nexty);
book[nextx][nexty] = true;
}
}
}
//跳出函数,说明相连的陆地已经搜索完啦,都打上了标记
}
int numIslands(char** grid, int gridSize, int* gridColSize) {
//需要岛屿数量,先定义
int res = 0;
//先初始化
for(int i = 0; i < gridSize; i++){
for(int j = 0; j < gridColSize[0]; j++){
book[i][j] = 0;
}
}
//有图,有大小,直接bfs
for(int i = 0; i < gridSize; i++){
for(int j = 0; j < gridColSize[0]; j++){
//未找过且是陆地
if(book[i][j] == 0 && grid[i][j] == '1'){
res++;//找到一块新的
// printf("%d %d %d-------\n", i, j, res);
//把周围的所有都标记上
bfs(i, j, gridSize, gridColSize[0], grid);
}
}
}
return res;
}
求最短路径
迪杰斯特拉
objectivec
#include<stdio.h>
#include<stdbool.h>
#include<limits.h>
#define N 505
int graph[N][N];//稠密图,邻接矩阵存储
int n, m;
bool book[N];//记录点是否在最终集合中
int d[N];//记录每个点到起点的距离
int main(){
scanf("%d %d", &n, &m);
//初始化
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
graph[i][j] = INT_MAX;
}
//初始化辅助数组
book[i] = false;
d[i] = INT_MAX;
}
//接收边
for(int i = 1; i <= m; i++){
int x, y, a;
scanf("%d %d %d", &x, &y, &a);
//可能存在重边
if(a < graph[x][y]) graph[x][y] = a;
}
//开始迪杰斯特拉
d[1] = 0;
for(int i = 0; i < n; i++){//找n个点,循环n次即可
int flag = -1;
for(int j = 1; j <= n; j++){//遍历点,从1到n
if(!book[j] && (flag == -1 || d[j] < d[flag])) flag = j;
}
//找到点,先标记
book[flag] = true;//第一次应该是第一个点标记
//更新其它点距离
for(int j = 1; j <= n; j++){
if(!book[j] && graph[flag][j] != INT_MAX && d[j] > d[flag] + graph[flag][j]){
//三个条件:1.没有在集合里 2.存在边 3.经过此点距离更短
d[j] = d[flag] + graph[flag][j];
}
}
}
//迪杰斯特拉结束
if(d[n] == INT_MAX){
printf("-1");
}else{
printf("%d", d[n]);
}
return 0;
}
贝尔曼
两点注意:
- 是对边进行松弛操作,所以有结构体边
- 松弛次数是距离起点边数的最短距离,所以一般松弛n - 1
- 如果有边数限制,则改变循环次数
- 如果要判断是否有负权回路,则要看看第n次松弛是否距离还在变
- 如果有负权回路,需要back数组
含负权最短距离
objectivec
#include<stdio.h>
#include<limits.h>
#define N 1010
#define M 10010
//floyd是对边进行松弛操作
struct edge{
int x;
int y;
int a;
}e[M];
int d[N];//记录每个点距离起点的距离
int main(){
int n, m;
scanf("%d %d", &n, &m);
//初始化距离
for(int i = 1; i <= n; i++){
d[i] = INT_MAX;
}
for(int i = 1; i <= m; i++){
int x, y, a;
scanf("%d %d %d", &x, &y, &a);
e[i].x = x; e[i].y = y; e[i].a = a;
}
// for(int i = 1; i <= m; i++){
// printf("%d %d %d\n", e[i].x, e[i].y, e[i].a);
// }
d[1] = 0;
//开始弗洛伊德
for(int i = 1; i < n; i++){
//松弛n - 1次,就可以找到所有的最短距离
//每一次都对所有边进行松弛
for(int j = 1; j <= m; j++){
int x = e[j].x; int y = e[j].y; int a = e[j].a;
if(d[x] != INT_MAX && d[y] > d[x] + a) d[y] = d[x] + a;
}
}
//松弛完就有最小距离啦
if(d[n] == INT_MAX){
printf("unconnected");
}else{
printf("%d", d[n]);
}
return 0;
}
判断是否存在负权回路
objectivec
#include<stdio.h>
#include<limits.h>
#include<stdbool.h>
#define N 1010
#define M 10010
//边
struct edge{
int x;
int y;
int a;
}e[M];
int main(){
int n, m;
scanf("%d %d", &n, &m);
for(int i = 1; i <= m; i++){
int x, y, a;
scanf("%d %d %d", &x, &y, &a);
e[i].x = x; e[i].y = y; e[i].a = a;
}
int d[N];
for(int i = 1; i <= n; i++){
d[i] = INT_MAX;
}
//开始
d[1] = 0;
bool flag = false;
for(int i = 1; i <= n; i++){
//循环n次,看看最后一次会不会有变化
for(int j = 1; j <= m; j++){
int x = e[j].x;
int y = e[j].y;
int a = e[j].a;
if(i < n){
if(d[x] != INT_MAX && d[y] > d[x] + a) d[y] = d[x] + a;
}else{
//第n次,看看变不变
if(d[x] != INT_MAX && d[y] > d[x] + a) flag = true;
}
}
}
if(flag){
printf("circle");
}else{
if(d[n] == INT_MAX){
printf("unconnected");
}else{
printf("%d", d[n]);
}
}
return 0;
}
含负权回路且限制边数的最短距离
objectivec
#include<stdio.h>
#include<limits.h>
#include<string.h>
#define N 505
#define M 10010
//边操作
struct edge{
int x;
int y;
int a;
}e[M];
int main(){
int n, m, k;
scanf("%d %d %d", &n,&m, &k);
for(int i = 1; i <= m; i++){
int x, y, a;
scanf("%d %d %d", &x, &y, &a);
e[i].x = x; e[i].y = y; e[i].a = a;
}
int d[N];
for(int i = 1; i <= n; i++){
d[i] = INT_MAX;
}
//开始弗洛伊德
d[1] = 0;
//存在负权回路时,需要备份数组
int back[N];
for(int i = 1; i <= k; i++){
//存在边数限制,则循环k次
memcpy(back, d, sizeof(d));//后面用back数组,不能用d
for(int j = 1; j <= m; j++){
int x = e[j].x; int y = e[j].y; int a = e[j].a;
if(back[x] != INT_MAX && d[y] > back[x] + a) d[y] = back[x] + a;
}
}
if(d[n] == INT_MAX){
printf("impossible");
}else{
printf("%d", d[n]);
}
return 0;
}
弗洛伊德
动态规划思想:用时间和空间换结果
每次都抽取一点作为中间点,看看距离是否变小,变小则更新,没变小就不动咯
多源最短路
objectivec
#include<stdio.h>
#define N 1005
int graph[N][N];
int min(int a, int b){
if(a < b){
return a;
}else{
return b;
}
}
int main(){
int n, m;
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
graph[i][j] = N;
}
}
for(int i = 1; i <= m; i++){
int x, y, a;
scanf("%d %d %d", &x, &y, &a);
//无向图
graph[x][y] = a;
graph[y][x] = a;
}
//floyd
for(int k = 1; k <= n; k++){
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
graph[i][j] = min(graph[i][j], graph[i][k] + graph[k][j]);
}
}
}
// for(int i = 1; i <= n; i++){
// for(int j = 1; j <= n; j++){
// printf("%d ", graph[i][j]);
// }
// printf("\n");
// }
int q;
scanf("%d", &q);
for(int i = 1; i <= q; i++){
int x, y;
scanf("%d %d", &x, &y);
if(graph[x][y] == 1005){
printf("-1\n");
}else{
printf("%d\n", graph[x][y]);
}
}
return 0;
}