二分图匹配

匈牙利算法

目前看到最清晰的题解

注:

匈牙利算法仅仅只针对左边的节点枚举,\(mat\) 存的是右边的节点对应左边的节点

\(vis\) 数组代表这一次增广路已经访问过的点,所以每次都要重置 \(vis\) 数组

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int n,m,e;
vector<int>b[N];
int mat[N];
bool vis[N];
bool find(int x){
    for(int v:b[x]){
        if(vis[v])  continue;
        vis[v]=1;//当前左节点是否被访问过
        if(!mat[v]||find(mat[v])){//本质上mat仅存的是右节点对应的左节点
            mat[v]=x;
            return 1;
        }
    }
    return 0;
}
int hungarian(int tot){
    int cnt=0;
    for(int i=1;i<=tot;i++){
        memset(vis,0,sizeof(vis));
        if(find(i))  cnt++;  
    }
    return cnt;
}
int main(){
    scanf("%d%d%d",&n,&m,&e);
    for(int i=1;i<=e;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        v+=n;
        b[u].push_back(v);
        b[v].push_back(u);
    }
    printf("%d\n",hungarian(n));
}

T1:

学了一大顿最小点覆盖怎么实现,但突然意识到,最小点覆盖不就等于最大匹配数吗,就求个值即可

T2:

调了一大顿

最大独立集等于点数-最小点覆盖

注:一般图最大独立集的方法还没找到,所以就考虑是不是二分图,发现可以用棋盘分割成二分图解决

注:匈牙利算法建边时只需要一侧点的边向另一侧连,所以这里只需要从黑点像白点建即可

感谢deepseek提供的trick:vis数组不用每次都清空,添加一个时间戳

如果90ptsTLE:建议从 \((i+j)%2==1\) 向外连边或许能过

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=40005,M=205;
const int dx[8]={-1,-2,+1,+2,-1,-2,+1,+2};
const int dy[8]={-2,-1,-2,-1,+2,+1,+2,+1};
int n,k,ans,t;
int mat[N],a[N],g[M][M],vis[N];
char c[N];
vector<int>b[N];
bool find(int x){
    if(!x)  return 0;
    for(int v:b[x]){
        if(vis[v]==t||!v)  continue;
        vis[v]=t;
        if(!mat[v]||find(mat[v])){
            mat[v]=x;
            return 1;
        }
    }
    return 0;
}
int id(int i,int j){
    if(i<1||i>n||j<1||j>n||g[i][j])  return 0;
    return  (i-1)*n+j;
}
int hungarian(int tot){
    int cnt=0;
    for(int i=1;i<=tot;i++){
        for(int j=1;j<=tot;j++){
            if((i+j)%2==1&&id(i,j)){
                t++;
                if(find(id(i,j)))  cnt++;
            }
        }
    }
    return cnt;
}
void add(int i,int j,int c){
    if(c==1)  return;
    ans++;
    for(int de=0;de<8&&(i+j)%2==1;de++){
        int nx=i+dx[de],ny=j+dy[de],sx=i,sy=j;
        if(id(nx,ny))  b[id(sx,sy)].push_back(id(nx,ny));
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%s",c);
        for(int j=1;j<=n;j++){
            g[i][j]=c[j-1]-'0';
            //add(i,j,c[j-1]-'0');
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            add(i,j,g[i][j]);//应当在这里加边,因为我们边的端点不能有障碍物,而前面加边不知道障碍物在哪
        }
    }
    printf("%d",ans-hungarian(n));
}

最小点覆盖问题

建议浏览这个

P2764

板子,好水一道紫(

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=505;
int n,m,t,cnt;
int vis[N],mat[N],h[N],to[N];
vector<int>b[N],ans[N];
bool find(int x){
    for(int v:b[x]){
        if(vis[v]==t)  continue;
        vis[v]=t;
        if(!mat[v]||find(mat[v])){
            mat[v]=x;
            to[x]=v;
            return 1;
        }
    }
    return 0;
}
int hungarian(int tot){
    int cnt=0;
    for(int i=1;i<=tot;i++){
        t++;
        if(find(i))  cnt++,h[i]=1;
    }
    return cnt;
}
void dfs(int x,int g){
    if(mat[x])  dfs(mat[x]+n,g);
    ans[g].push_back(x-n);
}
void sta(){
    for(int i=1;i<=n;i++){
        if(!h[i]){
            dfs(i+n,i);   
            for(int j:ans[i]){
                printf("%d ",j);
            }  
            printf("\n"); 
            cnt++;
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        b[u].push_back(v+n);
    }
    hungarian(n);
    sta();
    printf("%d\n",cnt);
}

有向图的最小路径覆盖

可以看这篇

T3:

模板题

点击查看代码
#include<bits/stdc++.h>
#define sx(x) dot[x].sx

#define sy(x) dot[x].sy

#define tx(x) dot[x].tx

#define ty(x) dot[x].ty

#define tim(x) dot[x].tim

using namespace std;
const int N=505;
int n,m,t,T;
int vis[N],mat[N];
struct taxi{
    int sx,sy,tx,ty,tim;
}dot[N];
vector<int>b[N];
bool find(int x){
    for(int v:b[x]){
        if(vis[v]==t)  continue;
        vis[v]=t;
        if(!mat[v]||find(mat[v])){
            mat[v]=x;
            return 1;
        }
    }
    return 0;
}
int hungarian(int tot){
    int cnt=0;
    for(int i=1;i<=tot;i++){
        t++;
        if(find(i))  cnt++;
    }
    return cnt;
}
int minit(char time[6]){
    int h=(time[0]-'0')*10+(time[1]-'0');
    int mi=(time[3]-'0')*10+(time[4]-'0');
    return h*60+mi;
}
int len(int a,int b,int c,int d){
    return abs(a-c)+abs(b-d);
}
void add(int i,int j){
    if(tim(i)>tim(j))  swap(i,j);
    int l=len(tx(i),ty(i),sx(j),sy(j))+len(sx(i),sy(i),tx(i),ty(i));
    if(tim(i)+l<tim(j))  b[i].push_back(j+n);//,printf("%d %d %d\n",tim(i),l,tim(j))
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&m);
        for(int i=1;i<=m;i++){
            char tim[6];
            scanf("%s",tim);
            tim(i)=minit(tim);
            scanf("%d%d%d%d",&sx(i),&sy(i),&tx(i),&ty(i));
        }
        for(int i=1;i<=m;i++){
            for(int j=i+1;j<=m;j++){
                add(i,j);
            }
        }
        printf("%d\n",m-hungarian(m));
        t=0;
        for(int i=1;i<=m;i++){
            vis[i]=mat[i]=0;
            b[i].clear();
        }
    }
}

posted @ 2025-07-26 09:56  daydreamer_zcxnb  阅读(9)  评论(0)    收藏  举报