🏆🏆🏆🏆🏆🏆🏆
欢迎观看我的博客,如有问题交流,欢迎评论区留言,一定尽快回复!(大家可以去看我的专栏,是所有文章的目录)
文章字体风格:
红色文字表示:重难点✔★
蓝色文字表示:思路以及想法✔★
如果大家觉得有帮助的话,感谢大家帮忙
点赞!收藏!转发!
我的qq号是:1210931886,欢迎大家加群,一起学习,互相交流,共同进步🎉🎉✨✨
🥇🥇🥇🥇🥇🥇🥇
蓝桥杯系列,为大家提供
- 做题全集,备战蓝桥杯,就做这个系列的题即可
 - 一个大概的做题规划——大家最好在此基础上提前两个月准备
 备战蓝桥杯就刷这些题
第一天博客链接 - 基础算法 -上
第二天博客链接 - 基础算法 -下 + 数据结构专题
第三天博客链接 - 搜索与图论-上 专题
第四天博客链接 - 搜索与图论-下 专题
第五天博客链接 - 数学知识专题
第六天博客链接 - 动态规划 专题
第七天博客链接 - 贪心算法 专题
蓝桥杯 刷题全集
- 9. spfa 算法
 - 1. spfa求最短路 ✔12.24
 - 做题总结:
 
- 10. spfa判断负权回路
 - 例题 spfa判断负环 ✔12.26
 - 刷题总结
 
- 11. floyd算法( 两两之间最短距离 )
 - 1. Floyd求最短路 ✔12.26
 - 做题总结
 
- 12. 朴素版prim算法
 - 1. Prim算法求最小生成树
 - 做题总结
 
- 13. Kruskal算法
 - 1. Kruskal算法求最小生成树( 利用并查集 )
 
- 14. 染色法判别二分图
 - 染色法判定二分图 ✔ 12.28
 - 算法思路 + 做题总结
 
- 15. 匈牙利算法
 - 模板
 - 二分图的最大匹配 ✔12.29
 - 做题总结:
 
9. spfa 算法
1. spfa求最短路 ✔12.24
原题链接
做题总结:
- 和宽搜差不多,只是可能会 返回走(但距离值更新了,就把这个节点入队列再处理一次)
 
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N = 1e5+10;
int e[N],ne[N],h[N],idx;
int w[N];
bool st[N];
int n,m;
void add(int x,int y,int c)
{
    e[idx] = y,ne[idx] = h[x],w[idx] = c,h[x] = idx++;
}
int d[N];
void spfa()
{
    memset(d,0x3f,sizeof d);
    d[1] = 0;
    queue<int> q;
    q.push(1);
    st[1] = true;
    while(q.size())
    {
        auto f = q.front();
        q.pop();
        st[f] = false;
        for(int i = h[f]; i != -1; i = ne[i])
        {
            int j = i;
            if(d[e[j]] > d[f] + w[j])
            {
                d[e[j]] = d[f] + w[j];
            if(st[e[j]]==false)
            {
                q.push(e[j]);
                st[e[j]] = true;
            }
                
            }
        }
    }
}
int main()
{
    cin >> n >> m;
    memset(h,-1,sizeof h);
    for(int i = 0; i < m; i++)
    {
        int x,y,c;
        cin >> x >> y >> c;
        add(x,y,c);
    }
    
    spfa();
    if(d[n] == 0x3f3f3f3f)
        cout << "impossible";
    else
        cout << d[n];
    return 0;
}
 
10. spfa判断负权回路
原题链接
- 什么是负环

图1中:2 到 3 到 4 到 2 路径长度为 -10
图2中:2 到 3 到 4 到 2 路径长度为 10 
图1才叫负环
 图2不是负环
-  
出现负环会怎么样
但出现负环的时候,如果我们要去求1到n的最短路,那么过程中,一定会在这个负环中一直转圈,导致路程可以变为负无穷 -  
怎么判断图中是否有负环?
综上,我们就采取求最小路径的方式(但是本题不是求最短路),当我们求最短路径的过程中,发现有一段路径重复走,那么就说明一定出现了负环 
问题来了:怎么判断某段路径在重复走
 我们想,1到n号点 最多才可能走了n-1条边
 如果我们发现 到某点时 已经走了 大于等于n条边,那么一定就是有负环了
 由于我们不知道 1 到 x点最多可能有多少条边,但一定不会超过 n - 1 条边,所以我们就都用 大于等于n条边去判断
例题 spfa判断负环 ✔12.26
原题链接
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 2010, M = 10010;
int n, m;
int h[N], w[M], e[M], ne[M], idx;
int dist[N], cnt[N];
bool st[N];
void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
bool spfa()
{
    queue<int> q;
    for (int i = 1; i <= n; i ++ )
    {
        st[i] = true;
        q.push(i);
    }
    while (q.size())
    {
        int t = q.front();
        q.pop();
        st[t] = false;
        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                cnt[j] = cnt[t] + 1;
                if (cnt[j] >= n) return true;
                if (!st[j])
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }
    return false;
}
int main()
{
    scanf("%d%d", &n, &m);
    memset(h, -1, sizeof h);
    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);
    }
    if (spfa()) puts("Yes");
    else puts("No");
    return 0;
}
 
刷题总结
- e,ne,h,idx 用于存储边,所以数值应该与边一样多
 - 把所有点都入队列,防止不是连通图
 - dist里存储多少都可以,因为我们只需判断负权回路
 - 当一个点所走的路径长度大于n,那么就一定有负边,因为最多就是n正常的话。
 - 一定要有st数组,判断是否再走这个点
 
11. floyd算法( 两两之间最短距离 )

1. Floyd求最短路 ✔12.26
原题链接
做题总结
- 用二维数组存储更方便
 - 读入存储的时候,读取最小值,并且到自身值为0
 - Floyd
 
#include<iostream>
#include<cstring>
using namespace std;
int n,m,k;
const int N = 210;
int d[N][N];
void Floyd()
{
    for(int k = 1; k <= n; k++)
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                d[i][j] = min(d[i][j],d[i][k]+d[k][j]);
}
int main()
{
    cin >> n >> m >> k;
    memset(d,0x3f,sizeof d);
    for(int i = 0; i < m; i++)
    {
        int x,y,c;
        cin >> x >> y >> c;
        d[x][y] = min(d[x][y], c);
        d[x][x] = 0;
        d[y][y] = 0;
    }
    Floyd();
    for(int i = 0; i < k; i++)
    {
        int x,y;
        cin >> x >> y;
        if(d[x][y]>=0x3f3f3f3f/2)
            cout << "impossible" << endl;
        else
            cout << d[x][y] << endl;
    }
    
    return 0;
}
 
12. 朴素版prim算法
1. Prim算法求最小生成树
原题链接
做题总结
1. 和dijk算法差不多 只是 dist数组存储的是 到联通块的距离
#include<iostream>
#include<cstring>
using namespace std;
const int N = 510;
int p[N][N], d[N];
int n, m;
bool st[N];
int res;
int sum;
void prim()
{
    memset(d, 0x3f, sizeof d);
    d[1] = 0;
    for (int k = 0; k < n; k++)
    {
        int t = -1;
        for (int l = 1; l <= n; l++)
        {
            if (st[l] == false && (t == -1 || d[t] > d[l]))
                t = l;
        }
        if (d[t] == 0x3f3f3f3f)
        {
            res++;
            return;
        }
        st[t] = true;
        sum += d[t];
        for (int i = 1; i <= n; i++)
        {
            d[i] = min(d[i], p[t][i]);
        }
    }
}
int main()
{
    memset(p,0x3f,sizeof p);
    cin >> n >> m;
    for (int i = 0; i < m; i++)
    {
        int x, y, c;
        cin >> x >> y >> c;
        p[x][y]= p[y][x] = min(p[x][y],c);
    }
    prim();
    if (res)
        cout << "impossible";
    else
        cout << sum;
    return 0;
}
 
13. Kruskal算法

1. Kruskal算法求最小生成树( 利用并查集 )
原题链接
 原题链接

#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010, M = 200010, INF = 0x3f3f3f3f;
int n, m;
int p[N];
struct Edge
{
    int a, b, w;
    bool operator< (const Edge &W)const
    {
        return w < W.w;
    }
}edges[M];
int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}
int kruskal()
{
    sort(edges, edges + m);
    for (int i = 1; i <= n; i ++ ) p[i] = i;    // 初始化并查集
    int res = 0, cnt = 0;
    for (int i = 0; i < m; i ++ )
    {
        int a = edges[i].a, b = edges[i].b, w = edges[i].w;
        a = find(a), b = find(b);
        if (a != b)
        {
            p[a] = b;
            res += w;
            cnt ++ ;
        }
    }
    if (cnt < n - 1) return INF;
    return res;
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; i ++ )
    {
        int a, b, w;
        scanf("%d%d%d", &a, &b, &w);
        edges[i] = {a, b, w};
    }
    int t = kruskal();
    if (t == INF) puts("impossible");
    else printf("%d\n", t);
    return 0;
}
 
14. 染色法判别二分图

染色法判定二分图 ✔ 12.28
算法思路 + 做题总结
算法思路
- 通过dfs 一个染1 另一个染2(通过3-c)
 - dfs需要有返回值。所以当 下一个返回来的是false,那么就返回false
 
所以一个dfs中,通过判断有一个return false,并且还有一个根据下一个的return 再return false
做题总结
- 无向图 需要开辟 2倍
 
原题链接
原题链接
#include<iostream>
#include<cstring>
using namespace std;
const int N = 2e5+10;
int e[N],ne[N],h[N],idx;
int color[N];
bool st[N];
int n,m;
void add(int a,int b)
{
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
bool dfs(int x,int c)
{
    st[x] = true;
    color[x] = c;
    for(int i = h[x]; i != -1; i = ne[i])
    {
        int j = e[i];
        if(st[j]==true)
        {
            if(color[j]==color[x])
                return false;
        }
        if(st[j] == false)
        {
            if(!dfs(j,3-c))
                return false;
        }
    }
    return true;
}
int main()
{
    cin >> n >> m;
    memset(h,-1,sizeof h);
    
    for(int i = 0; i < m; i++)
    {
        int x,y;
        cin >> x >> y;
        add(x,y),add(y,x);
    }
    
    bool flag = false;
    for(int i = 1; i <= n; i++)
    {
        if(st[i]==false)
        {
            if(!dfs(i,1))
            {
                flag = true;
                break;
            }
        }
    }
    
    if(flag == true)
        cout << "No";
    else
        cout << "Yes";
    
    return 0;
}
 
15. 匈牙利算法
模板
时间复杂度是 O(nm)O(nm), nn 表示点数,mm 表示边数
int n1, n2;     // n1表示第一个集合中的点数,n2表示第二个集合中的点数
int h[N], e[M], ne[M], idx;     // 邻接表存储所有边,匈牙利算法中只会用到从第一个集合指向第二个集合的边,所以这里只用存一个方向的边
int match[N];       // 存储第二个集合中的每个点当前匹配的第一个集合中的点是哪个
bool st[N];     // 表示第二个集合中的每个点是否已经被遍历过
bool find(int x)
{
    for (int i = h[x]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j])
        {
            st[j] = true;
            if (match[j] == 0 || find(match[j]))
            {
                match[j] = x;
                return true;
            }
        }
    }
    return false;
}
// 求最大匹配数,依次枚举第一个集合中的每个点能否匹配第二个集合中的点
int res = 0;
for (int i = 1; i <= n1; i ++ )
{
    memset(st, false, sizeof st);
    if (find(i)) res ++ ;
}
 
二分图的最大匹配 ✔12.29
原题链接
做题总结:
- 避免多次重复问一个女生,所以遍历每次男生时,用st存储该男生是否考虑过这个女生,这样其他男生就别再考虑了
 










![[多线程进阶] 常见锁策略](https://img-blog.csdnimg.cn/e61a5be559734a7b805f4af11dff154e.png)








