c语言闯算法--图(1)

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;
}

贝尔曼

两点注意:

  1. 是对边进行松弛操作,所以有结构体边
  2. 松弛次数是距离起点边数的最短距离,所以一般松弛n - 1
    1. 如果有边数限制,则改变循环次数
    2. 如果要判断是否有负权回路,则要看看第n次松弛是否距离还在变
    3. 如果有负权回路,需要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;
}
相关推荐
CYRUS_STUDIO33 分钟前
安卓实现魔改版 Base64 算法
android·算法·逆向
一只_程序媛1 小时前
【leetcode hot 100 142】环形链表Ⅱ
算法·leetcode·链表
Luis Li 的猫猫1 小时前
基于MATLAB的冰块变化仿真
开发语言·图像处理·人工智能·算法·matlab
郭涤生1 小时前
并发操作的同步_第四章_《C++并发编程实战》笔记
开发语言·c++·算法
深思慎考1 小时前
Linux——进程间通信(system V共享内存)
linux·服务器·算法
加减法原则2 小时前
求最大子数组和 | LeetCode刷题
算法
折枝寄北2 小时前
从零开始 | C语言基础刷题DAY1
c语言·开发语言·算法
.ccl3 小时前
蓝桥杯省赛真题C++B组2024-握手问题
c++·算法·蓝桥杯
arong_xu3 小时前
C++20 新特性总结
算法·c++20
hrrrrb3 小时前
【C语言】指针篇
c语言·数据结构·算法