P2055 [ZJOI2009]假期的宿舍(二分图&最大流)

这篇博客介绍了如何利用二分图和最大流算法解决假期宿舍中人与床的匹配问题。通过建立合适的图结构,分别采用两种不同的方法实现匹配:一种是直接遍历并寻找增广路径,另一种是采用 Dinic 算法构建网络流模型。文章提供了详细的代码实现,展示了如何在 O(n^2m) 的时间复杂度内找到最佳匹配方案。

P2055 [ZJOI2009]假期的宿舍(二分图&最大流)

人和床的匹配,关键是建图。

首先在校且不回家的人 add(i,i)

然后如果i和j认识,并且j在学校,那么建边add(i,j)

然后对于(不在学校或者(在学校不回家的))人进行匹配。

时间复杂度:O(nm)O(nm)O(nm)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull; 
const int N=55,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
const int hashmod[4] = {402653189,805306457,1610612741,998244353};
#define mst(a,b) memset(a,b,sizeof a)
#define db double
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define x first
#define y second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=a;i>=b;--i)
#define IOS ios::sync_with_stdio(false),cin.tie(nullptr) 
void Print(int *a,int n){
	for(int i=1;i<n;i++)
		printf("%d ",a[i]);
	printf("%d\n",a[n]); 
}
template <typename T>		//x=max(x,y)  x=min(x,y)
void cmx(T &x,T y){
	if(x<y) x=y;
}
template <typename T>
void cmn(T &x,T y){
	if(x>y) x=y;
}
struct node{
	int to,nt;
}e[M];
int cnt,h[N];
int n;
void add(int u,int v){
	e[++cnt]={v,h[u]},h[u]=cnt;
}
int vis[N],mh[N];
int id;
bool find(int u){
	for(int i=h[u];i;i=e[i].nt){
		if(vis[e[i].to]==id) continue;
		int v=e[i].to;
		vis[v]=id;
		if(!mh[v]||find(mh[v])){
			mh[v]=u;
			return true;
		}
	}
	return false;
}
int in[N],ho[N];
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		id = cnt = 0;
		scanf("%d",&n);
		mst(h,0);
		mst(mh,0);
		mst(vis,0);
		rep(i,1,n) scanf("%d",&in[i]);
		rep(i,1,n){
			scanf("%d",&ho[i]);
			if(in[i]&&!ho[i]){
				add(i,i);
			}
		}
		rep(i,1,n)
			rep(j,1,n){
				int x;scanf("%d",&x);
				if(x && in[j]) add(i,j);
			}
		int ok = 1;
		rep(i,1,n){
			if(!in[i]||(in[i]&&!ho[i])){
				++id;
				if(!find(i)){
					ok = 0;
					break;
				}
			}
		}
		puts(ok?"^_^":"T_T");
	}
	return 0;
}

做法2:网络流

源点连需要床的人 i 。

汇点连学校的人。

如果i 和j认识,且 j在学校, i就连j的床。

注意N开两倍,因为 人和床都要标号。

时间复杂度:O(n2m)O(n^2m)O(n2m)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull; 
const int N=105,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
const int hashmod[4] = {402653189,805306457,1610612741,998244353};
#define mst(a,b) memset(a,b,sizeof a)
#define db double
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define x first
#define y second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=a;i>=b;--i)
#define IOS ios::sync_with_stdio(false),cin.tie(nullptr) 
void Print(int *a,int n){
	for(int i=1;i<n;i++)
		printf("%d ",a[i]);
	printf("%d\n",a[n]); 
}
template <typename T>		//x=max(x,y)  x=min(x,y)
void cmx(T &x,T y){
	if(x<y) x=y;
}
template <typename T>
void cmn(T &x,T y){
	if(x>y) x=y;
}
struct Dinic{
//Dinic O(n^2m)
	int n,m,st,ed;
	int id(int x,int y){
		return (x-1)*m+y;
	}
	struct edge{
		int to,nt;
		ll w;
	}e[M];
	int h[N],cur[N],cnt,dep[N];
	void init(int _st,int _ed){
		st=_st,ed=_ed;
		cnt=1;mst(h,0);
	}
	Dinic(int _st=0,int _ed=0){init(_st,_ed);}
	void add(int u,int v){
		e[++cnt]={v,h[u],1},h[u]=cnt;
		e[++cnt]={u,h[v],0},h[v]=cnt;
	}
	ll dfs(int u,ll c){	//search for augment path
		if(u==ed) return c;
		ll res=c;
		for(int &i=cur[u];i;i=e[i].nt){
			int v=e[i].to; ll w=e[i].w;
			if(w&&dep[v]==dep[u]+1){
				ll now=dfs(v,min(res,w));
				if(!now) dep[v]=1;
				else e[i].w-=now,e[i^1].w+=now,res-=now;
			}
			if(!res) return c;
		}return c-res;
	} 
	bool bfs(){		//layer the graph
		queue<int>q;q.push(st);mst(dep,0),dep[st]=1;
		while(!q.empty()){
			int u=q.front();q.pop();cur[u]=h[u];
			for(int i=h[u];i;i=e[i].nt){
				int v=e[i].to;ll w=e[i].w;
				if(w&&!dep[v]) dep[v]=dep[u]+1,q.push(v);
			}
		}return dep[ed];
	} 
	ll dinic(){
		ll s=0;
		while(bfs()) s+=dfs(st,inf);
		return s;
	}
}G;
int in[N],ho[N];
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		int n;scanf("%d",&n);
		int ed=2*n+1;
		int sum = 0;
		G.init(0,ed);
		rep(i,1,n) scanf("%d",&in[i]);
		rep(i,1,n){
			scanf("%d",&ho[i]);
			if(!in[i]||(in[i]&&!ho[i])){
				sum++;
				G.add(0,i);
			}
			if(in[i]){
				G.add(i+n,ed);
			}
			if(in[i]&&!ho[i]){
				G.add(i,i+n);
			}
		}
		rep(i,1,n){
			rep(j,1,n){
				int x;scanf("%d",&x);
				if(x&&in[j]){
					G.add(i,j+n);
				}
			}
		}
		int ok = G.dinic()==sum;
		puts(ok?"^_^":"T_T");
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

酷酷的Herio

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值