算法集训----图论 备课笔记

一、图的存储

  • 邻接矩阵 (顶点数<1000) a[i][j] = 1

当输入数据是0 1矩阵时,适合采用邻接矩阵法

  • 邻接表 (vector存储相邻顶点)v[i] = {2,3,4}
  • 结构体数组存边 edges[i] = Edge(x,y,z)
  • 链式前向星 (head数组初始化-1 add函数添加边)
void add(int u, int v, int w){
    edges[ct].val = w;
    edges[ct].to = v;
    edges[ct].next = head[u];
    head[u] = ct++;
}

当输入输入格式为:
n m
x1 y1 z1

xm ym zm
或者输入格式为
n m
x1 y1

xm ym
上面四种都可以

二、图的遍历

  • DFS 深度优先搜索 (栈)
  • BFS 广度优先搜索 (队列)

三、最短路径

  • Dijkstra算法(迪杰斯特拉算法) 单源最短路径 贪心
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 101;
int a[MAXN][MAXN];
int book[MAXN];
int dis[MAXN];
int n, m;
int Dijkstra(){
    for(int i = 1; i <= n; i++){
        dis[i] = a[1][i];
        book[i] = 0;
    }
    book[1] = 1;
    for(int i = 1; i < n; i++){
        int u = 2, MIN = INT_MAX;
        for(int j = 2; j <= n; j++){
            if(book[j] == 0 && dis[j] < MIN){
                u = j;
                MIN = dis[j];
            }
        }
        book[u] = 1;
        for(int j = 2; j <= n; j++){
            if(a[u][j] < INT_MAX && book[j] == 0 && dis[j] > dis[u] + a[u][j]){
                dis[j] = dis[u]+a[u][j];
            }
        }
    }
    return dis[n];
}

int main(){
    while(cin >> n >> m){
        if(n == 0 && m == 0)break;
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                if(i != j)
                    a[i][j] = INT_MAX;
                else a[i][j] = 0;
            }
        }
        for(int i = 1; i <= m; i++){
            int x, y, z;
            cin >> x >> y >> z;
            a[x][y] = a[y][x] = z;
        }
        int sum = Dijkstra();
        cout << sum << endl;
    }
}
  • Floyd算法(佛洛依德算法)多源最短路算法 动态规划
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 101;
int a[MAXN][MAXN];
int n, m;
void Floyd(){
    for(int k = 1; k <= n; k++){
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                if(a[i][j] > a[i][k] + a[k][j]){
                    a[i][j] = a[i][k] + a[k][j];
                }
            }
        }
    }
}

int main(){
    while(cin >> n >> m){
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                if(i != j)
                    a[i][j] = 9999999;
                else a[i][j] = 0;
            }
        }
        for(int i = 1; i <= m; i++){
            int x, y, z;
            cin >> x >> y >> z;
            if(a[x][y] > z)
            a[x][y] = a[y][x] = z;
        }
        Floyd();
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                if(j!=1){
                    cout << " ";
                }
                cout << a[i][j];
            }
            cout << endl;
        }
    }
}

四、最小生成树

  • prim算法(普里姆算法) 贪心
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 101;
int a[MAXN][MAXN];
int dis[MAXN];//每个点到源点的距离
int book[MAXN];//每个点是否加入到最小生成树中
int n;
int Prim(){
    for(int i = 1; i <= n; i++){
        dis[i] = INT_MAX;
    }
    for(int i = 1; i <= n; i++){
        book[i] = 0;
    }
    dis[1] = 0;
    book[1] = 1;
    for(int i = 1; i < n; i++){
        int u=1, MIN = INT_MAX;//寻找离最小生成树距离最短的点
        for(int j = 2; j <= n; j++){
            if(book[j] == 0 && dis[j] < MIN){
                MIN = dis[j];
                u = j;
            }
        }
        book[u] = 1;
        for(int j = 1; j <= n; j++){
            if(book[j] == 0 && dis[j] > a[u][j]){
                dis[j] = a[u][j];
            }
        }
    }
    int sum = accumulate(dis+1, dis+n+1,0);
    return sum;
}
int main(){
//    ifstream cin("input.txt");
//    ofstream cout("output.txt");
    while(cin >> n ){
        if(n == 0)break;
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                a[i][j] = 0;
            }
        }
        for(int i = 1; i <= n * ( n - 1) / 2; i++){
            int x, y, z;
            cin >> x >> y >> z;
            a[x][y] = a[y][x] = z;
        }
        int sum = Prim();
        cout << sum << endl;
    }
}
  • Kruskal算法 (克鲁斯卡尔算法)并查集
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 101;
int n;
struct Edge{
    int x,y,z;
    bool operator<(const Edge&that)const{
        return z < that.z;
    }
};
Edge edges[MAXN*MAXN];
int F[MAXN];
int getf(int x){
    if(x == F[x]){
        return F[x];
    }else{
        F[x] = getf(F[x]);
        return F[x];
    }
}
int Kruskal(){
    int cnt = 0, sum = 0;
    for(int i = 1; i <= n * ( n - 1) / 2; i++){
        int t1 = getf(edges[i].x);
        int t2 = getf(edges[i].y);
        if(t1 != t2){
            F[t2] = t1;
            cnt++;
            sum+=edges[i].z;
            if(cnt>=n-1){
                break;
            }
        }
    }
    if(cnt == n-1){
        return sum;
    }else{
        return -1;
    }
}
int main(){
//    ifstream cin("input.txt");
//    ofstream cout("output.txt");
    while(cin >> n ){
        if(n == 0)break;
        for(int i = 1; i <= n; i++){
            F[i] = i;//并查集初始化
        }
        for(int i = 1; i <= n * ( n - 1) / 2; i++){
            int x, y, z,flag;
            cin >> edges[i].x >> edges[i].y >> edges[i].z>>flag;//存边
            if (flag == 1)//这一步很关键,表示已经修好的路
                edges[i].z = 0;
        }
        sort(edges+1, edges+n * ( n - 1) / 2+1);
        int sum = Kruskal();
        cout << sum << endl;
    }
}

五、注意事项

  • 边的优化
    当点的数量为n时,最小生成树的边为n-1,两两相邻的点至少需要有n*(n-1)/2条边。
    注意读入边的时候,是否有重复的边,根据需要过滤掉重复的边
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值