【题解】[CQOI2008] 传感器网络/二分图匹配最小字典序

我是个调了一下午的蒟蒻

待会来讲一下这个算法的原理。

理论上,这个做法可以处理所有 二分图匹配最小字典序 问题。

#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)]);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值