Living-Dream 系列笔记 第89期

继续学习二分图最大匹配。

P10937

「行列匹配考虑建二分图。」

考虑将行视为左部点,列视为右部点,车视为边,则转化为一个最大匹配模型,即最多(车放置的数量最多)且不共点(每个车都不互相攻击)。做完了。

code
#include<bits/stdc++.h>
using namespace std;
const int N=2e2+5;
int n,m,t,id;
int vis[N];
bool no[N][N];
int match[N];
vector<int> G[N];
bool hungary(int cur){
if(vis[cur]==id)
return 0;
vis[cur]=id;
for(int i:G[cur]){
if(match[i]==0||hungary(match[i])){
match[i]=cur;
return 1;
}
}
return 0;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m>>t;
for(int i=1,u,v;i<=t;i++){
cin>>u>>v;
no[u][v]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(no[i][j]==0)
G[i].push_back(j);
int ans=0;
for(int i=1;i<=n;i++){
++id;
if(hungary(i))
++ans;
}
cout<<ans;
return 0;
}

P1129

仍然考虑上面的建图方式,那么交换操作就可以看作是将一堆缠绕着的「线」展开,最终达成 \(1 \to 1,2 \to 2,...,n \to n\)(即所有棋子都在主对角线上)的情形。如图。

image

image

于是乎,只要最大匹配数为 \(n\) 则有解,否则无解。

code
#include<bits/stdc++.h>
using namespace std;
const int N=2e2+5;
int n,m,t,id;
int vis[N];
int match[N];
vector<int> G[N];
bool hungary(int cur){
if(vis[cur]==id)
return 0;
vis[cur]=id;
for(int i:G[cur]){
if(match[i]==0||hungary(match[i])){
match[i]=cur;
return 1;
}
}
return 0;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>t;
while(t--){
for(int i=1;i<=n;i++)
G[i].clear();
memset(match,0,sizeof match);
memset(vis,0,sizeof vis);
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int c; cin>>c;
if(c)
G[i].push_back(j);
}
}
int ans=0; id=0;
for(int i=1;i<=n;i++){
++id;
if(hungary(i))
++ans;
}
cout<<(ans==n?"Yes\n":"No\n");
}
return 0;
}

P2055

显然的,人向床连边。

具体的,在校且不回家的向自己和认识的人连,外校的向认识的人连,回家的不考虑。

然后就是如果最大匹配数为所有在校且不回家以及外校的人(注意不是 \(n\)!!!)就有解,否则无解。

code
#include<bits/stdc++.h>
using namespace std;
const int N=4e3+5;
int n,m,t,id;
int vis[N];
bool on[N],ok[N];
int match[N];
vector<int> G[N];
void init(){
id=0;
memset(on,0,sizeof on);
memset(ok,0,sizeof ok);
memset(match,0,sizeof match);
memset(vis,0,sizeof vis);
for(int i=1;i<=n;i++)
G[i].clear();
}
bool hungary(int cur){
if(vis[cur]==id)
return 0;
vis[cur]=id;
for(int i:G[cur]){
if(match[i]==0||hungary(match[i])){
match[i]=cur;
return 1;
}
}
return 0;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>t;
while(t--){
init();
cin>>n;
for(int i=1;i<=n;i++){
cin>>on[i];
}
for(int i=1,f;i<=n;i++){
cin>>f;
if(on[i]&&f==0)
ok[i]=1;
}
for(int i=1;i<=n;i++){
for(int j=1,f;j<=n;j++){
cin>>f;
if(f&&on[j]&&(ok[i]||on[i]==0))
G[i].push_back(j);
}
}
for(int i=1;i<=n;i++)
if(ok[i])
G[i].push_back(i);
int ans=0,all=0;
for(int i=1;i<=n;i++){
if(ok[i]||on[i]==0)
all++;
++id;
if(hungary(i))
++ans;
}
cout<<(ans==all?"^_^":"T_T")<<'\n';
}
return 0;
}

P2526

必经点和景点连边,前提是通过景点的路径长度不超过两个必经点之间距离的两倍。

跑匈牙利算法即可,然后答案就是所有必经点加上可能的景点,注意为了输出方便需要记一个反向的 match。

code
#include<bits/stdc++.h>
#define double long double
#define eps 1e-10
using namespace std;
const int N=1e2+5;
int n,m,id;
int vis[N],ml[N],mr[N];
pair<double,double> a[N],b[N];
vector<int> G[N];
vector<pair<double,double> > ans;
bool hungary(int cur){
if(vis[cur]==id)
return 0;
vis[cur]=id;
for(int i:G[cur]){
if(ml[i]==0||hungary(ml[i])){
ml[i]=cur,mr[cur]=i;
return 1;
}
}
return 0;
}
double g(double x,double y,double xx,double yy){
return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i].first>>a[i].second;
for(int i=1;i<=m;i++)
cin>>b[i].first>>b[i].second;
for(int i=1;i<n;i++){
double d1=g(a[i].first,a[i].second,a[i+1].first,a[i+1].second);
for(int j=1;j<=m;j++){
double d2=g(a[i].first,a[i].second,b[j].first,b[j].second);
double d3=g(a[i+1].first,a[i+1].second,b[j].first,b[j].second);
if(d2+d3-2.0*d1<=eps)
G[i].push_back(j);
}
}
for(int i=1;i<n;i++)
++id,hungary(i);
for(int i=1;i<=n;i++){
ans.push_back({a[i].first,a[i].second});
if(mr[i])
ans.push_back({b[mr[i]].first,b[mr[i]].second});
}
cout<<ans.size()<<'\n';
for(auto i:ans)
cout<<i.first<<' '<<i.second<<' ';
return 0;
}

总结:

  • 如何看出二分图?题中有明显图论性质(类似于攻击、排斥等)且只有两个点集,抑或是行列这种经典模型。

  • 如何想到最大匹配?有一个满足要求最多且不共点的东西,它就是边/匹配。抓住两个最大匹配的关键性质即可。

  • 如何进一步思考?左部点 / 右部点 / 连边,往这三个方面想准没错。

  • 菜就多练。

posted @   _XOFqwq  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示