二分图带权匹配-KM算法

例题:牛客多校第五场-room

const int MAXN=105;
int w[MAXN][MAXN];//边权
int la[MAXN],lb[MAXN];//左、右部点的顶标
bool va[MAXN],vb[MAXN];//访问标记:是否在交错树中
int match[MAXN];//右部点匹配了哪一个左部点
int n,delta;

bool dfs(int x){
    va[x]=1;//访问标记,x在交错树中
    for(int y=1;y<=n;y++){
        if(vb[y]) continue;
        if(la[x]+lb[y]-w[x][y]==0){//相等子图
            vb[y]=1;//访问标记:y在交错树中
            if(!match[y]||dfs(match[y])){
                match[y]=x;
                return true;
            }
        }else delta=min(delta,la[x]+lb[y]-w[x][y]);
    }
    return false;
}

int KM(){
    for(int i=1;i<=n;i++){
        la[i]=-(1<<30);//-INF
        lb[i]=0;
        for(int j=1;j<=n;j++){
            la[i]=max(la[i],w[i][j]);
        }
    }
    for(int i=1;i<=n;i++){
        while(true){  //直到左部点找到匹配
            memset(va,0,sizeof(va));
            memset(vb,0,sizeof(vb));
            delta = 1<<30;//INF
            if(dfs(i)) break;
            for(int j=1;j<=n;j++){//修改顶标
                if(va[j]) la[j]-=delta;
                if(vb[j]) lb[j]+=delta;
            }
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++) ans+=w[match[i]][i];
    return ans;
}

void init(){
    memset(match,0,sizeof(match));
}

猜你喜欢

转载自blog.csdn.net/algzjh/article/details/81390023