我是个调了一下午的蒟蒻
待会来讲一下这个算法的原理。
理论上,这个做法可以处理所有 二分图匹配最小字典序 问题。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int N=55*55;
//判断是否存在匹配包含 (u,v)
//看是否存在环
//若存在环,将路径全部取反
//**然后删掉 (u,v) 这条边**
//适用于完美匹配 -> 拆点 + 建虚点
int n,match[N],dfn[N],l[N],r[N],cnt,num,in[N],id[N];
char ch[55];
vector<int> g[N],g2[N];
//dfn 标记打在右部节点
bool dfs(int u) {
for(auto v:g2[u]) {
if(dfn[v]==num) continue;
dfn[v]=num;
if(match[v]==-1||dfs(match[v])) {
match[v]=u;
return 1;
}
}
return 0;
}
int dfs(int u,int topf) {
for(auto v:g2[u]) {
if(dfn[v]==num) continue;
dfn[v]=num;
if(match[v]==topf||(match[v]>topf&&dfs(match[v],topf)!=-1)) {
match[v]=u;
return v;
}
}
return -1;
}
bool check(int mid) {
cnt=0;
for(int i=0;i<n;i++) in[i]=mid;
in[n]=n;
for(int i=0;i<=n;i++) {
l[i]=cnt;
for(int j=1;j<=in[i];j++) {
id[cnt++]=i;
}
r[i]=cnt;
}
for(int i=0;i<n;i++) {
g2[i].clear();
}
for(int i=0;i<n;i++) {
for(auto j:g[i]) {
for(int k=l[j];k<r[j];k++) {
g2[i].push_back(k);
}
}
}
memset(match,-1,sizeof match);
for(int i=0;i<=n-1;i++) {
num++;
if(!dfs(i)) return 0;
}
return 1;
}
int main() {
scanf("%d",&n);
scanf("%s",ch);
for(int i=0;i<n;i++) {
char Ch[55]; scanf("%s",Ch);
for(int j=0;j<n;j++) {
if(Ch[j]=='Y') {
g[i].push_back(j);
}
}
}
for(int i=0;i<n;i++) {
if(ch[i]=='Y') {
g[i].push_back(n);
}
}
int l=0,r=n,res=0;
while(l<=r) {
int mid=(l+r)/2;
if(check(mid)) res=mid,r=mid-1;
else l=mid+1;
}
check(res);
for(int i=n;i<cnt;i++) {
for(int j=0;j<cnt;j++) {
g2[i].push_back(j);
}
dfs(i);
}
for(int i=0;i<n;i++) {
num++;
printf("%d ",id[dfs(i,i)]);
}
}