欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

搜索与图论板子库

程序员文章站 2022-06-18 10:10:13
搜索与图论搜索与图论#DFS##全排列##n皇后#BFS##走迷宫##八数码#树与图的深度优先遍历##树的重心#树与图的广度优先遍历##图中点的层次#拓扑排序##有向图的拓扑序列#dijkstra##一##二#bellman-ford#spfa#floyd#prim#kruskal#染色法判定二分图#匈牙利算法这里的python全是python3的详情请见acwing,acwing赛高力求快、准、狠、短搜索与图论#DFS##全排列##n皇后#BFS##走迷宫##八数码#树与图的深度优...

这里的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

相关标签: 算法模板