搜索与图论板子库

2022-07-31,

搜索与图论

  • 搜索与图论
    • #DFS
      • ##排列与组合之类的
        • ----c++版
      • ##n皇后
        • ----c++版
    • #BFS
      • ##走迷宫
        • ----c++版
      • ##八数码
        • ----c++版
    • #树与图的深度优先遍历
      • ##树的重心
        • ----c++版
    • #树与图的广度优先遍历
      • ##图中点的层次
        • ----c++版
    • #拓扑排序
      • ##有向图的拓扑序列
        • ----c++版
    • #dijkstra
      • ##一
        • ----c++版
      • ##二 堆优化版
        • ----c++版
    • #bellman-ford
      • ##有边数限制的最短路
        • ----c++版
    • #spfa
      • ##spfa求最短路
        • ----c++版
      • ##spfa判断负环
        • ----c++版
    • #floyd
      • ----c++版
    • #prim
      • ##Prim算法求最小生成树
        • ----c++版
    • #kruskal
      • ##kruskal算法求最小生成树
        • ----c++版
    • #染色法判定二分图
      • ----c++版
    • #匈牙利算法,二分图的最大匹配
      • ----c++版

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


搜索与图论

#DFS

##排列与组合之类的

----c++版

全排列
https://www.acwing.com/problem/content/844/

#include<iostream>
using namespace std;
const int N = 10;

int n;
int path[N];
bool st[N];

void dfs(int u){
    if(u==n){
        for(int i=0;i<n;i++)printf("%d ",path[i]);
        puts("");
        return;
    }
    for (int i=1;i<=n;i++)
        if(!st[i]){
            path[u]=i;
            st[i]=true;
            dfs(u+1);
            st[i]=false;//不需要拔开path的,因为每次都会覆盖掉
        }
}
int main(){
    cin>>n;
    dfs(0);
    return 0;
}

全组合
https://www.acwing.com/problem/content/description/95/
递归写法

#include<iostream>
using namespace std;
const int N=30;
int n,m;
bool st[N];
int path[N];

void comb(int u){
    if(u==m){
        for(int i=0;i<m;i++)printf("%d ",path[i]);
        puts("");
        return;
    }
    for(int i=1;i<=n;i++){
        if(u&&i<path[u-1])continue;//古典概型的列组合方法,后面一定比前面大
        if(!st[i]){
            st[i]=true;
            path[u]=i;
            comb(u+1);
            st[i]=false;
        }
    }
}

int main(){
    cin>>n>>m;
    comb(0);
    return 0;
}

##n皇后

----c++版

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

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

const int N=20;
int n;
char g[N][N];
bool col[N],dg[N],udg[N];
void dfs(int u){
    if(u==n){
        for(int i=0;i<n;i++)puts(g[i]);
        puts("");
        return;
    }
    for(int i=0;i<n;i++){
        if(!col[i]&&!dg[u+i]&&!udg[n-u+i]){
            g[u][i]='Q';
            col[i]=dg[u+i]=udg[n-u+i]=true;
            dfs(u+1);
            col[i]=dg[u+i]=udg[n-u+i]=false;
            g[u][i]='.';
        }
    }
}
int main(){
    cin>>n;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            g[i][j]='.';
    dfs(0);return 0;
}

#BFS

##走迷宫

----c++版

https://www.acwing.com/problem/content/description/846/

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=110;
typedef pair<int, int> PLL;

int n,m;
int g[N][N];
int d[N][N];
PLL q[N*N];

int bfs(){
    int hh=0,tt=0;
    q[0]={0,0};
    memset(d,-1,sizeof d);
    d[0][0]=0;
    int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
    
    while(hh<=tt){
        auto t=q[hh++];
        
        for(int i=0;i<4;i++){
            int x=t.first+dx[i],y=t.second+dy[i];
            if(x>=0&&x<n&&y>=0&&y<m&&g[x][y]==0&&d[x][y]==-1){
                d[x][y]=d[t.first][t.second]+1;
                q[++tt]={x,y};
            }
        }
    }
    return d[n-1][m-1];
}

int main(){
    cin>>n>>m;
    for(int i=0;i<n;i++)
       for(int j=0;j<m;j++)
           cin>>g[i][j];
           
    cout<<bfs();
    
    return 0;
}

##八数码

----c++版

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

//状态表示
//状态转移
//路径存储
#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<queue>
#include<cstring>
using namespace std;

int bfs(string start){
    string end = "12345678x";//终点
    
    queue<string>q;
    unordered_map<string, int>d;//距离数组
    
    q.push(start);
    d[start]=0;//unordered_map的用法
    
    int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};//对称写法
    
    while(q.size()){
        auto t=q.front();
        q.pop();
        int distance=d[t];//distance为当前节点的步数
        if(t==end)return distance;
        //状态转移
        int k=t.find('x');
        int x=k/3,y=k%3;//小技巧,一维数组下标转换为二维数组下标
        
        for(int i=0;i<4;i++){
            int a=x+dx[i],b=y+dy[i];
            if(a>=0&&a<3&&b>=0&&b<3){
                swap(t[k], t[a*3+b]);//状态转移,获得新状态
                //走过的一定是最短的
                if(!d.count(t)){//如果没走过
                    d[t]=distance+1;//加上距离
                    q.push(t);//加入待拓展队列
                }
                swap(t[k], t[a*3+b]);
            }
        }
    }
    return -1;
}

int main(){
    string state;
    for(int i=0;i<9;i++){
        char c;
        cin>>c;
        state+=c;
    }
    
    cout<<bfs(state)<<endl;
    
    return 0;
}

#树与图的深度优先遍历

树是无环连接图,图分为有向图和无向图,无向图是特殊的有向图
有向图的存储可以用邻接矩阵(存稠密图,有重边的只留一条)和邻接表
图的遍历可以通过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;
}

#dijkstra

##一

----c++版

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

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=510;
int n,m;
int g[N][N];
int dist[N];//从1走到n号点的当前最短距离
bool st[N];//确定了最短路的点的集合

int dijkstra(){
    memset(dist, 0x3f, sizeof dist);
    dist[1]=0;
    for(int i=0;i<n;i++){//迭代n次
        int t=-1;//每次找出没确定最短路的点之中到 确定最短路的点的集合 的距离最小的点
        for(int j=1;j<=n;j++)
            if(!st[j]&&(t==-1||dist[t]>dist[j]))
                t=j;
        st[t]=true;//将找到的点加入确定最短路的点的集合
        if(t==n)break;
        for(int j=1;j<=n;j++)//实际上有m次
            dist[j]=min(dist[j],dist[t]+g[t][j]);//用新的点的最短路去尝试更新所有当前最短路(也包括曾确定的)
    }
    if(dist[n]==0x3f3f3f3f)return -1;
    return dist[n];
}

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]=min(g[a][b], c);//不用管自环,用不到
    }
    int t=dijkstra();
    printf("%d",t);
    return 0;
}

##二 堆优化版

在朴素版中有【找出没确定最短路的点之中到 确定最短路的点的集合 的距离最小的点】这个操作,
朴素版用循环实现,堆优化直接用优先队列实现,省去了循环的过程

----c++版

你可以手写堆,这样堆里可以维持n个数,也可以使用c++提供的优先队列,但队列里可能有m个数
个人觉得堆优化版的dijkstra更好理解,更直接
https://www.acwing.com/problem/content/852/

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
//稀疏图用邻接表存
const int N=1e6+10;
int n,m;
int h[N],e[N],w[N],ne[N],idx;
int dist[N];
bool st[N];
typedef pair<int, int>pll;//由于我们还需要知道节点编号

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

int dijkstra(){
    memset(dist, 0x3f, sizeof dist);
    dist[1]=0;
    priority_queue<pll, vector<pll>, greater<pll>>heap;//小根堆
    heap.push({0,1});//1到1最短距离是0
    while(heap.size()){
        auto t=heap.top();
        heap.pop();
        int ver=t.second,distance=t.first;
        if(st[ver])continue;st[ver]=true; //在集合内就丢掉
        for(int i=h[ver];i!=-1;i=ne[i]){
            int j=e[i];//更新这个点能走到的所有点的最短距离
            if(dist[j]>distance+w[i]){
                dist[j]=distance+w[i];
                heap.push({dist[j],j});//把更新过的最短路加到堆里,虽然更短路会被堆pop掉,但没关系,因为distj是确实更新了,且堆只是为了找还在集合外的点
            }
        }
    }
    if(dist[n]==0x3f3f3f3f)return -1;
    return dist[n];
}

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);
    }
    int t=dijkstra();
    printf("%d",t);
    return 0;
}

#bellman-ford

bellman-ford 的存边方式比较简单,只要能遍历到所有边就行,可以用最简单的开个结构体的方式
基本是两重循环

for 遍历n次:
    for 从a到b权值w的所有边:
        dist[b]=min(dist[b], dist[a]+w);

每次判断1到b的距离能不能用这条边来更新
遍历完保证对所有边满足dist[b]<=dist[a]+w, 这是松弛操作
bellman-ford可以处理负权边
如果有负权回路,最短路不一定存在

外层循环迭代k次,dist数组的含义是:不超过k条边到各点的最短路距离
如果第n次迭代还有边被更新,说明存在一条最短路上面有n条边,由于最多才n个点,抽屉原理,所有必然有两个点编号一样,存在负环。

有可能有负环又存在最短路的,
如果用spfa,则一定要求图中没有负环

##有边数限制的最短路

这个只能用bellman-ford,一般spfa比bellman-ford好

----c++版

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

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=510,M=10010;
int n,m,k;
int dist[N],backup[N];
struct edge{
    int a,b,w;
}edges[M];

int bellmanford(){
    memset(dist, 0x3f, sizeof dist);
    dist[1]=0;
    for(int i=0;i<k;i++){
        //备份dist数组,因为可能出现串联
        //即在此次更新过程中用此次更新的距离去更新其他距离,这样限制不了步数
        //需要在每次更新时都使用上一次迭代的结果
        memcpy(backup, dist, sizeof dist);
        for(int j=0;j<m;j++){
            int a=edges[j].a,b=edges[j].b,w=edges[j].w;
            dist[b]=min(dist[b],backup[a]+w);
        }
    }
    //大于一个比较大的数,因为到不了的点之间的负权边可能会把无穷大更新了
    if(dist[n]>0x3f3f3f3f/2)return -1;
    return dist[n];
}

int main(){
    scanf("%d%d%d",&n,&m,&k);
    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=bellmanford();
    if(t==-1)puts("impossible");
    else printf("%d", t);
    return 0;
}

#spfa

网格型图易卡spfa

##spfa求最短路

对bellman-ford的优化
针对松弛操作的优化,如果dist[b]变小了,一定是因为dist[a]变小了,
如果有变小的距离,针对这个距离把之后的全部更新就得
更新过谁,再拿谁来更新别人

----c++版

实现和dijkstra很像
https://www.acwing.com/problem/content/853/

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef pair<int ,int> pll;
const int N=100010;
int n,m;
int h[N],w[N],e[N],ne[N],idx;
int dist[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++;
}
//类似宽搜的方式
int spfa(){
    memset(dist, 0x3f, sizeof dist);
    dist[1]=0;
    queue<int>q;
    q.push(1);
    st[1]=true;//存当前点是否在队列中
    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];
                if(!st[j]){//如果已经在就不用再加了
                    q.push(j);
                    st[j]=true;
                }
            }
        }
    }
    if(dist[n]==0x3f3f3f3f)return -1;
    return dist[n];
}

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);
    }
    int t=spfa();
    if(t==-1)puts("impossible");
    else printf("%d",t);
    return 0;
}

##spfa判断负环

----c++版

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

// 思路和bellmanford差不多,抽屉原理,时间复杂度比较高
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;

typedef pair<int ,int> pll;
const int N=100010;
int n,m;
int h[N],w[N],e[N],ne[N],idx;
int dist[N], cnt[N];//cnt记录步数,
bool st[N];

void add(int a,int b,int c){
    e[idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx++;
}
//类似宽搜的方式
int 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;
}

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

本文地址:https://blog.csdn.net/lafea/article/details/107558527

《搜索与图论板子库.doc》

下载本文的Word格式文档,以方便收藏与打印。