题目
大意:n*m格子有墙和陷阱,墙能遮挡视线,往空地上放守卫保证守卫不能互相看到
Solution
比较经典的匹配问题,将行和列分段(跟据墙)分别设为左部点和右部点,空地代表行和列有连边,然后跑二分图最大匹配就完了。
Code
第一次上传code稍微加一下注释吧~
//By zuiyumeng
#pragma GCC optimize(2)
#include <set>
#include <map>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define Re register
#define Ms(a,b) memset((a),(b),sizeof(a))
#define Fo(i,a,b) for(Re int i=(a),_=(b);i<=_;i++)
#define Ro(i,a,b) for(Re int i=(b),_=(a);i>=_;i--)
// #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin)),p1==p2?EOF:*p1++)
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
char buf[1<<21],*p1,*p2;
inline int read() {
int x=0,f=1;char c=getchar();
while(!isdigit(c)) {if(c=='-')f=-f;c=getchar();}
while(isdigit(c)) x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
//上面是常用头文件可忽略qwq
const int N=210;
int n,m,cntl,cntn,now,ans;
int a[N][N],b[N][N],lx[N][N];//a为原数组(0空地1陷阱2墙),b为列号,lx为行号(用c会报错= =)
int head[N*N*2],vis[N*N*2],mat[N*N*2],nxt[N*N*2],to[N*N*2];//mat表示匹配点
//邻接表算不清楚的话尽量开大一些TAT,否则容易RE
void add(int u,int v) {
nxt[++cntl]=head[u]; to[cntl]=v; head[u]=cntl;
nxt[++cntl]=head[v]; to[cntl]=u; head[v]=cntl;
}
bool hun(int u) {//匈牙利算法~
for(int i=head[u],v;i;i=nxt[i]) {
if(vis[(v=to[i])]==now) continue;
vis[v]=now;
if(!mat[v] || hun(mat[v])) {mat[v]=u; return 1;}
}
return 0;
}
int main() {
n=read(),m=read();
Fo(i,1,n) Fo(j,1,m) a[i][j]=read();
Fo(j,1,m) {
int tmp=++cntn;
Fo(i,1,n) {
if(a[i][j]==2) tmp=cntn=cntn+(a[i-1][j]!=2);//尽量减少点的数量防止RE(结果还是RE了一次= =)
else if(a[i][j]==0) b[i][j]=tmp;
}
}
Fo(i,1,n) {
int tmp=++cntn;
Fo(j,1,m) {
if(a[i][j]==2) tmp=cntn=cntn+(a[i][j-1]!=2);
else if(a[i][j]==0) lx[i][j]=tmp,add(tmp,b[i][j]);
}
}
now=1;
for(int i=1;i<=cntn;i++,now++) if(hun(i)) ans++;
printf("%d
",ans/2);
Fo(i,1,n) Fo(j,1,m) if(mat[b[i][j]]&&mat[b[i][j]]==lx[i][j]) printf("%d %d
",i,j);
return 0;
}
后记
既然做了一道二分图,那么接下来就整理一下二分图的知识吧qwq~