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\)(即所有棋子都在主对角线上)的情形。如图。
于是乎,只要最大匹配数为 \(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; }
总结:
-
如何看出二分图?题中有明显图论性质(类似于攻击、排斥等)且只有两个点集,抑或是行列这种经典模型。
-
如何想到最大匹配?有一个满足要求最多且不共点的东西,它就是边/匹配。抓住两个最大匹配的关键性质即可。
-
如何进一步思考?左部点 / 右部点 / 连边,往这三个方面想准没错。
-
菜就多练。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】