ybtoj Au 二分图匹配
前言中的前言
-
由于本人过菜,有些题解会咕掉,请原谅这个蒟蒻
-
由于本人过菜,不知道什么时候就
了,想给这个机房留下点什么…… -
如果想看高效进阶的题解,建议出门左拐,去云落那里看看,保证是全网最全最好的,但不要对云落的博客好奇,更不要看云落的一言 和 云落的合集:黑夜刀己,白日爱人
正片开始!
好耶!!!(泪目)
题面
前言
二分图 T1,板子题
正文
求一个最小点覆盖,等于最大匹配数
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+6;
int n,k,head[N],cnt,p[N];
struct node{int to,nxt;}e[N];
void add(int u,int v){
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
bool vis[N];
bool match(int x){
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(!vis[y]){
vis[y]=1;
if(p[y]==0||match(p[y])){
p[y]=x;
return 1;
}
}
}
return 0;
}
int main(){
cin>>n>>k;
for(int i=1;i<=k;i++){
int x,y;
cin>>x>>y;
add(x,y+n),add(y+n,x);
}
int cnt=0;
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
if(match(i))cnt++;
}
cout<<cnt;
return 0;
}
后记
秒力
题面
前言
二分图 T2,应用题
正文
首先,黑白染色,那么马可以攻击到的格子是与他颜色不一样的
那么我们把马能到的点都连上边,此时就是要求二分图的最大独立集
答案即为点数-最大匹配数
代码
#include <bits/stdc++.h>
using namespace std;
const int N=205;
const int dx[9]={0,-1,-2,1,2,-1,-2,1,2};
const int dy[9]={0,-2,-1,-2,-1,2,1,2,1};
int n,cnt,head[N*N*2],cnt1;
bool vis[N*N*2];
struct node{int to,nxt;}e[N*N*2];
char s[N][N];
void add(int u,int v){
e[++cnt1].to=v;
e[cnt1].nxt=head[u];
head[u]=cnt1;
}
int ans,p[N*N*2];
bool dfs(int x){
for(int i=head[x];i;i=e[i].nxt){
int v=e[i].to;
if(vis[v])continue;
vis[v]=1;
if(!p[v]||dfs(p[v])){
p[v]=x;
return 1;
}
}
return 0;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>(s[i]+1);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(s[i][j]=='1'){
cnt++;
continue;
}
for(int k=1;k<=8;k++){
int x=i+dx[k],y=j+dy[k];
if(s[x][y]=='1')continue;
if(x>=1&&x<=n&&y>=1&&y<=n&&(i+j)%2)add((i-1)*n+j,(x-1)*n+y);
}
}
}
for(int i=1;i<=n*n;i++){
memset(vis,0,sizeof(vis));
if(dfs(i))ans++;
}
cout<<n*n-cnt-ans;
return 0;
}
后记
重温了一下自己以前写的文(好尬),当然,现在的我写的文在将来的我看来也很尬吧
题面
前言
二分图 T3,应用题
正文
若
代码
#include <bits/stdc++.h>
using namespace std;
const int N=505;
struct node{int a,b,c,d,t;}p[N];
int n,h,m,cnt,head[N*N],o[N];
struct Node{int to,nxt;}e[N*N];
void add(int u,int v){
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
char c;
int dis(int i,int j){
return abs(p[i].c-p[j].a)+abs(p[i].d-p[j].b);
}
bool vis[N];
bool dfs(int x){
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(vis[y])continue;
vis[y]=1;
if(!o[y]||dfs(o[y])){
o[y]=x;
return 1;
}
}
return 0;
}
int main(){
int t;
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++){
cin>>h>>c>>m>>p[i].a>>p[i].b>>p[i].c>>p[i].d;
p[i].t=h*60+m;
//cout<<p[i].t<<endl;
}
cnt=0;
memset(head,0,sizeof(head));
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(p[i].t+dis(i,i)+dis(i,j)<p[j].t){
add(i,j);
//cout<<i<<" "<<j<<endl;
}
}
}
int ans=0;
memset(o,0,sizeof(o));
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
if(dfs(i))ans++;
}
cout<<n-ans<<endl;
}
return 0;
}
后记
博客没保存……
题面
前言
二分图 T4,应用题
正文
结论:在这个博弈中,如果初始在二分图的所有最大匹配上,那么先手必胜,反之后手必胜。
其中加粗的点为在任意最大匹配上的点。不妨先称为“粗点”,相对地有“细点”。
可以发现,从一个粗点出发,只需要任意选择一个在任意一个最大匹配上的匹配点。
只要先手走的是最大匹配,无论后手怎么走,因为题目要求不能回头,所以一定可以继续沿着一个最大匹配走。
梳理一下这是怎么回事:只要先手在粗点,那么无论对方怎么走,永远可以再次走最大匹配。如果不可以,这就与最大匹配的“最大”矛盾了。
反之,如果在细点上,那么后手一定可以在一个粗点上出发,即后手必胜。
所以,题目所求的就转化为:求在一个二分图上,有哪些点不一定在最大匹配上。
只要求出这个细点集,空(也就是说,存在完美匹配,任意点皆为粗点)即输出 LOSE,非空输出 WIN 即可。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=200005;
const int dx[5]={0,-1,0,1,0};
const int dy[5]={0,0,1,0,-1};
int n,m,x[N],y[N],X,Y,head[N],cnt;
char s[205][205];
struct node{int to,nxt;}e[N];
void add(int u,int v){
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
bool flag,vis[N],mark[N];
int p[N];
bool dfs(int x){
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(!vis[y]){
vis[y]=1;
if(dfs(p[y])||!p[y]){
p[y]=x;
return 1;
}
}
}
return 0;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>(s[i]+1);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(s[i][j]=='.'){
if((i+j)%2){
x[++X]=i*m+j;
for(int k=1;k<=4;k++){
int xx=i+dx[k],yy=j+dy[k];
if(s[xx][yy]=='.'){
add(i*m+j,xx*m+yy);
add(xx*m+yy,i*m+j);
}
}
}else{
y[++Y]=i*m+j;
}
}
}
}
for(int i=1;i<=X;i++){
memset(vis,0,sizeof(vis));
if(!dfs(x[i])){
flag=1;
mark[x[i]]=1;
}
}
for(int i=1;i<=Y;i++){
if(!p[y[i]]){
mark[y[i]]=1;
flag=1;
}else{
memset(vis,0,sizeof(vis));
vis[y[i]]=1;
if(dfs(p[y[i]])){
p[y[i]]=0;
mark[y[i]]=1;
flag=1;
}
}
}
for(int i=1;i<=Y;i++){
memset(vis,0,sizeof(vis));
if(!dfs(y[i])){
flag=1;
mark[y[i]]=1;
}
}
for(int i=1;i<=X;i++){
if(!p[x[i]]){
mark[x[i]]=1;
flag=1;
}else{
memset(vis,0,sizeof(vis));
vis[x[i]]=1;
if(dfs(p[x[i]])){
p[x[i]]=0;
mark[x[i]]=1;
flag=1;
}
}
}
if(!flag){cout<<"LOSE"<<endl;return 0;}
cout<<"WIN"<<endl;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(mark[i*m+j]){
cout<<i<<" "<<j<<endl;
}
}
}
return 0;
}
后记
好难好难
题面
前言
二分图 T5,水题
正文
把原来有 “
把能合并的连边
代码
#include <bits/stdc++.h>
using namespace std;
const int N=2e6+6;
int n,m,ans,op,head[N],cnt,p[N];
bool flag[N],vis[N];
char s[N];
struct node{int to,nxt;}e[N];
void add(int u,int v){
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
bool dfs(int x){
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(vis[y])continue;
vis[y]=1;
if(!p[y]||dfs(p[y])){
p[y]=x;
return 1;
}
}
return 0;
}
int main(){
while(cin>>n>>m,n){
memset(flag,0,sizeof(flag));
memset(head,0,sizeof(head));
memset(p,0,sizeof(p));
op=ans=cnt=0;
for(int i=1;i<=m;i++){
cin>>s;
int tmp=0,pos=-1;
for(int j=0;j<n;j++){
if(s[j]=='1')tmp+=1<<j;
else if(s[j]=='*')pos=j;
}
flag[tmp]=1;
if(~pos)flag[tmp+(1<<pos)]=1;
}
for(int i=0;i<(1<<n);i++){
if(flag[i]){
op++;
for(int j=0;j<n;j++){
if(flag[i^(1<<j)])add(i,i^(1<<j));
}
}
}
for(int i=0;i<(1<<n);i++){
memset(vis,0,sizeof(vis));
if(dfs(i))ans++;
}
cout<<op-ans/2<<endl;
}
return 0;
}
后记
洛谷会 T,不理解
题面
前言
二分图 T6,应用题
正文
求最大独立集
(最长反链:在反链上
代码
#include <bits/stdc++.h>
using namespace std;
int n,m,f[205][205],ans,head[40005],cnt,p[40005];
bool vis[40005];
struct node{int to,nxt;}e[40005];
void add(int u,int v){
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
bool dfs(int x){
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(vis[y])continue;
vis[y]=1;
if(!p[y]||dfs(p[y])){
p[y]=x;
return 1;
}
}
return 0;
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
f[u][v]=1;
}
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
f[i][j]|=f[i][k]&f[k][j];
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(f[i][j]){
add(i,j+n),add(j+n,i);
}
}
}
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
if(dfs(i))ans++;
}
cout<<n-ans;
return 0;
}
后记
去洛谷上水黑题吧(但要输出方案)
题面
前言
二分图 T7,应用题
正文
如果他是二维的,那么直接用第一题的思路就行
观察到
钦定是
这样就暴力枚举
代码
#include <bits/stdc++.h>
using namespace std;
int t,minn=0x7f7f7f7f;
const int N=5005;
int op[4][N],m,ans,cnt,opp,head[N],p[N],a,b,c;
struct node{int to,nxt;}e[N];
void add(int u,int v){
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
bool oi[N],vis[N];
bool dfs(int x){
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(vis[y])continue;
vis[y]=1;
if(!p[y]||dfs(p[y])){
p[y]=x;
return 1;
}
}
return 0;
}
void check(int x){
memset(head,0,sizeof(head));
memset(p,0,sizeof(p));
cnt=0,opp=0;
for(int i=0;i<a;i++){
if(x&(1<<i))oi[i+1]=0,opp++;
else oi[i+1]=1;
}
for(int i=1;i<=m;i++){
if(oi[op[1][i]])add(op[2][i],op[3][i]);
}
for(int i=1;i<=b;i++){
memset(vis,0,sizeof(vis));
if(dfs(i))opp++;
}
ans=min(opp,ans);
}
int main(){
cin>>t;
while(t--){
m=0;
ans=0x7f7f7f7f;
cin>>a>>b>>c;
minn=min(a,min(b,c));
for(int i=1;i<=a;i++){
for(int j=1;j<=b;j++){
for(int k=1;k<=c;k++){
int o;
cin>>o;
if(!o)continue;
op[1][++m]=i;
op[2][m]=j;
op[3][m]=k;
}
}
}
if(minn==b)swap(a,b),swap(op[1],op[2]);
else if(minn==c)swap(a,c),swap(op[1],op[3]);
for(int i=0;i<(1<<a);i++){
check(i);
}
cout<<ans<<endl;
}
return 0;
}
后记
好难好难
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】