搜索与图论板子库

这里的python全是python3的
详情请见acwing,acwing赛高
力求快、准、狠、短


关于搜索

#DFS

DFS可以找到路径,但不能保证最短
可以解决基于连通性的问题

##排列与组合之类的

https://blog.csdn.net/lafea/article/details/107723419

##DFS之连通性与搜索顺序

https://blog.csdn.net/lafea/article/details/109165283

##DFS之剪枝与优化

https://blog.csdn.net/lafea/article/details/109165380

##DFS之迭代加深、双向dfs、IDA*优化

https://blog.csdn.net/lafea/article/details/109116292

#BFS

具体不用多说了吧??细节看模板

BFS主要有两类

  1. 求最短距离:给定地图,求最短路径
    如 走迷宫
  2. 求最小步数:一个地图的许多种状态,求一个状态变为另一种状态的最少变化步数
    如 八数码

BFS可以解决

  1. 求最小
  2. 基于迭代的算法,不会爆栈

##flood-filled模型

https://blog.csdn.net/lafea/article/details/109164637

##最短路模型

https://blog.csdn.net/lafea/article/details/109164723

##多源BFS、最小步数模型、双端队列广搜

https://blog.csdn.net/lafea/article/details/109164885

##双向广搜、A*优化

https://blog.csdn.net/lafea/article/details/109164979

#关于图论

#关于单源最短路问题

⭐–单源最短路的基础模板–⭐

#单源最短路问题的建图方式

https://blog.csdn.net/lafea/article/details/109493293

#单源最短路问题的综合应用

https://blog.csdn.net/lafea/article/details/110188239

#树与图的深度优先遍历

树是无环连接图,图分为有向图和无向图,无向图是特殊的有向图
有向图的存储可以用邻接矩阵(存稠密图,有重边的只留一条)和邻接表
图的遍历可以通过bfs和dfs

##树的重心

----c++版

https://www.acwing.com/problem/content/848/

#include<iostream>
#include<algorithm>
#include<cstring> # 使用memset
using namespace std;

const int N=1e5+10,M=N*2;//无向图,每个节点间有两条有向边
int n,m;
int h[N],e[M],ne[M],idx;
bool st[N];//存哪些点已经遍历过了
int ans=N;

void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

int dfs(int u){
    st[u]=true;//标记一下已经搜过
    int sum=1,res=0;
    for(int i=h[u];i!=-1;i=ne[i]){
        int j=e[i];
        if(!st[j]){
            int s=dfs(j);
            res= max(res,s);
            sum+=s;
        }
    }
    res=max(res,n-sum);//树枝和树干比
    ans=min(ans,res);//如果当前节点是重心,则当前遍历到的节点的最大子树的权值就是解答
    //不需要先知道谁是重心,一个一个节点判断就好,
    //如果一个节点的最大子树的权值在所有之中最小,这个节点就是重心
    return sum;
}

int main(){
    cin>>n;memset(h, -1, sizeof h);
    for(int i=0;i<n-1;i++){
        int a,b;cin>>a>>b;
        add(a,b),add(b,a);
    }
    dfs(1);
    cout<<ans<<endl;
    return 0;
}

#树与图的广度优先遍历

##图中点的层次

----c++版

https://www.acwing.com/problem/content/849/

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N=1e5+10;
int n,m;
int h[N],e[N],ne[N],idx;
int d[N],q[N];

void add(int a,int b){
    e[idx]=b;ne[idx]=h[a];h[a]=idx++;
}

int bfs(){
    int hh=0,tt=0;
    q[0]=1;
    memset(d, -1, sizeof d);//-1表示没有被遍历过
    d[1]=0;//本身距离是零
    while(hh<=tt){
        int t=q[hh++];
        for(int i=h[t];i!=-1;i=ne[i]){
            int j=e[i];
            if(d[j]==-1){
                d[j]=d[t]+1;
                q[++tt]=j;
            }
        }
    }
    return d[n];
}

int main(){
    cin>>n>>m;
    memset(h, -1, sizeof h);
    for(int i=0;i<m;i++){
        int a,b;cin>>a>>b;
        add(a,b);
    }
    cout<<bfs()<<endl;
    return 0;
}

#拓扑排序

##有向图的拓扑序列

图的宽搜应用,针对有向图
在图中存在一种序列,使得按序列所有的有向边都指向一个方向

在这里插入图片描述
有向无环图(拓扑图)一定存在一个拓扑序列,一定有一个入度为0的点。
反证法:如果一个有n个有限点的有向无环图没有入度为0的点,每个点必然可以由入度不为0的地方找到上一个点,当找的点数大于n,由抽屉原理,必然存在一个环路,矛盾

入度:有几条边指向;出度:有几条边出去
所有入度为零的点都能排在最前边的位置

----c++版

https://www.acwing.com/problem/content/850/

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n,m;
int h[N],e[N],ne[N],idx;
int q[N],d[N];//d存每个点的入度

void add(int a,int b){
    e[idx]=b;ne[idx]=h[a];h[a]=idx++;
}

bool topsort(){
    int hh=0,tt=-1;
    for(int i=1;i<=n;i++)
        if(!d[i])
            q[++tt]=i;//把所有入度为零的点先加入队列
    while(hh<=tt){
        int t=q[hh++];
        for(int i=h[t];i!=-1;i=ne[i]){
            int j=e[i];
            d[j]--;
            if(d[j]==0)q[++tt]=j;
        }
    }
    return tt==n-1;//从0开始的
}

int main(){
    cin>>n>>m;
    memset(h, -1, sizeof h);
    for(int i=0;i<m;i++){
        int a,b;cin>>a>>b;
        add(a,b);
        d[b]++;
    }
    if(topsort()){
        for(int i=0;i<n;i++)printf("%d ",q[i]);
    }else puts("-1 ");
    return 0;
}

#floyd

----c++版

https://www.acwing.com/problem/content/856/

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
//可以负权,不能负权回路
const int N=210, inf=1e9;
int n,m,Q;
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(){
    scanf("%d%d%d",&n ,&m ,&Q );
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(i==j)d[i][j]=0;
            else d[i][j]=inf;
    while(m--){
        int a,b,w;scanf("%d%d%d",&a,&b,&w);
        d[a][b]=min(d[a][b],w);
    }
    
    floyd();
    
    while(Q--){
        int a,b;scanf("%d%d",&a,&b);
        if(d[a][b]>inf/2)puts("impossible");//非通路负权边的更新问题
        else printf("%d\n",d[a][b]);
    }
    return 0;
}

#prim

##Prim算法求最小生成树

----c++版

https://www.acwing.com/problem/content/860/

//朴素prim和dijkstra很像
//不同之处在于dijkstra是用t来更新其他点到起点的距离
//prim用t更新其他点到集合的距离
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=510, inf=0x3f3f3f3f;
int n,m;
int g[N][N];
int dist[N];//存到集合的距离
bool st[N];

int prim(){
    int res=0;
    memset(dist , 0x3f, sizeof dist);
    for(int i=0;i<n;i++){
        int t=-1;
        for(int j=1;j<=n;j++){
            if(!st[j]&&(t==-1||dist[t]>dist[j]))
                t=j;
        }
        if(i&&dist[t]==inf)return inf;//不是第一个点了且距离inf,不存在
        if(i)res += dist[t];//先加再更新,不然有自负环的会出错把自己更新掉
        for(int j=1;j<=n;j++)dist[j]=min(dist[j], g[t][j]);        
        st[t]=true;
    }
    return res;
}

int main(){
    scanf("%d%d",&n,&m);
    memset(g, 0x3f, sizeof g);
    while(m--){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        g[a][b]=g[b][a]=min(g[a][b], c);//建路连等式
    }
    int t=prim();
    if(t==inf)puts("impossible");
    else printf("%d",t);
    return 0;
}

#kruskal

稀疏图

##kruskal算法求最小生成树

----c++版

https://www.acwing.com/problem/content/861/

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
//由于要给边排序,o(mlogm),算法极限了。kruskal比较直接
//不需要存图,开个结构体存边就行
const int N=200010;
int n,m;
int p[N];
struct edge{
    int a,b,w;
    bool operator<(const edge &W)const{
        return w<W.w;
    }
}edges[N];

int find(int x){
    if(p[x]!=x)p[x]=find(p[x]);
    return p[x];
}//递归版

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};
    }
    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)puts("impossible");//注意边界
    else printf("%d", res);
    return 0;
}

#染色法判定二分图

----c++版

https://www.acwing.com/problem/content/862/

//二分图:可以把所有点划到两边,每边集合中没有边
//判断二分图,当且仅当图中不含奇数环(环的边数是奇数)
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e6+10,M=2000010;
int n,m;
int h[N],e[N],ne[N],idx;
int color[N];

void add(int a,int b){
    e[idx]=b;ne[idx]=h[a];h[a]=idx++;
}

bool dfs(int u,int c){
    color[u]=c;
    for(int i=h[u];i!=-1;i=ne[i]){
        int j=e[i];
        if(!color[j]){
            if(!dfs(j, 3-c))return false;
        }
        else if(color[j]==c)return false;
    }
    return true;
}

int main(){
    scanf("%d%d",&n,&m);
    memset(h, -1,sizeof h);
    while(m--){
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);add(b,a);
    }
    bool flag=true;
    for(int i=1;i<=n;i++)
        if(!color[i]){
            if(!dfs(i,1)){
                flag=false;
                break;
            }
        }
    
    if(flag)puts("Yes");
    else puts("No");
    return 0;
}

#匈牙利算法,二分图的最大匹配

----c++版

https://www.acwing.com/problem/content/863/

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=510,M=100010;
int n1,n2,m;
int h[N],e[M],ne[M],idx;
bool st[N];
int match[N];

void add(int a,int b){
    e[idx]=b;ne[idx]=h[a];h[a]=idx++;
}

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 main(){
    scanf("%d%d%d",&n1,&n2,&m);
    memset(h, -1, sizeof h);
    while(m--){
        int a,b;scanf("%d%d",&a,&b);
        add(a,b);//只用连一边的点的
    }
    int res=0;
    for(int i=1;i<=n1;i++){
        memset(st, false, sizeof st);
        if(find(i))res++;
    }
    printf("%d\n",res);
    return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值