题解 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;
}
细节实现
- 为什么是二分图?
人和床属于两类不同对象,匹配关系单向
- 为什么用匈牙利算法?
求最大匹配是否等于需求人数
- 极端情况:
所有非本校生都不认识任何床主人 → 无解
总结归纳
通过这种分步拆解,能快速建立“问题→模型→代码”的映射能力。

浙公网安备 33010602011771号