题解 luogu.P2055 [ZJOI2009] 假期的宿舍

题目

luogu.P2055 [ZJOI2009] 假期的宿舍
本题分属于二分图及最大匹配的问题,给出题目如上。

题意建模

‌第一步:理解题意‌

‌题目描述‌:

学校有 \(n\) 个学生,部分学生是本校生(有固定床位),部分是非本校生。假期期间:

每个本校生可以选择是否留校
所有非本校生都需要住宿
每个人(包括留校的本校生)只能睡自己的床或认识的人的床
问:能否满足所有人的住宿需求?

‌第二步:识别二分图要素‌

按照建模四步法分析:

‌两类对象‌

  • 左部集:需要住宿的人(留校本校生 + 所有非本校生)
  • 右部集:可用的床(本校生的床)

‌边的关系‌

当人i可以睡床j时建边:

  • 情况1:\(i\) 是床 \(j\) 的主人(自己睡自己的-床)
  • 情况2:\(i\) 认识床 \(j\) 的主人(且主人允许使用)
  • ‌匹配目标‌:最大匹配数 = 需要住宿的人数 → 存在完美匹配

算法分析

‌第四步:关键点解析‌

‌左部节点‌:

(!school[i] || !home[i]) 的人

  • 非本校生 !school[i]
  • 本校生但选择留校 !home[i]

‌右部节点‌:

所有本校生的床 school[v] == 1

‌建边条件‌:

know[u][v] == 1(认识床的主人)

‌特例处理‌:

允许自己睡自己的床(know[i][i]默认应为1)

参考代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e2+5;
int match[N],vis[N],timestamp;
int school[N],home[N],know[N][N];
int t,n,ans;
void init()
{
	/*memset(school,0,sizeof(school));
	memset(home,0,sizeof(home));
	memset(konw,0,sizeof(konw));
	ans=0;
	timestamp=0;*/
	ans=0;
	memset(match,0,sizeof(match));
}
bool hungary(int u)
{
	for(int v=1;v<=n;v++)
	{
		if(!know[u][v]||vis[v]==timestamp||!school[v]) continue;
		vis[v]=timestamp;
		if(!match[v] || hungary(match[v]))
		{
			match[v]=u;
			return true;
		}
	}
	return false;
}
void solve()
{
	init();
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&school[i]);
	for(int i=1;i<=n;i++) scanf("%d",&home[i]);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++) scanf("%d",&know[i][j]);
	for(int i=1;i<=n;i++) know[i][i]=1;
	bool flag=true;
	for(int i=1;i<=n;i++) 
		if(!school[i]||!home[i])
		{
			timestamp++;
			if(!hungary(i)) { flag=false; break; }
		}
	printf("%s\n",flag?"^_^":"T_T");
}
int main()
{
	scanf("%d",&t);
	while(t--) solve();
	return 0;
}

细节实现

- 为什么是二分图‌?

人和床属于两类不同对象,匹配关系单向

- 为什么用匈牙利算法‌?

求最大匹配是否等于需求人数

- 极端情况‌:

所有非本校生都不认识任何床主人 → 无解

总结归纳

通过这种分步拆解,能快速建立“问题→模型→代码”的映射能力。
posted @ 2025-07-25 18:16  枯骨崖烟  阅读(11)  评论(0)    收藏  举报