[HEOI2016/TJOI2016]游戏
壹、题目
贰、思考
两个炸弹不能同在一行一列,除非有硬石头 #
隔开。软石头 x
不能放东西,也挡不住炸弹 真没用 。
如果没有硬石头,那么如果有个炸弹能放在 \(\lang x,y\rang\),就 \(x\rightarrow y\) 连一条边,边数有 \(n^2\) 条,点有 \(n\) 个,可以直接跑匈牙利.
但是现在有硬石头了,它可以挡住炸弹,建虚点?但是有另外的问题.
叁、题解
我们单横着看,一个硬石头将两段分开,变成了独立的两段,竖着看,硬石头的效果也是一样,所以我们可以考虑将这一段看成一个点,横竖分开编号。
如果我们在横竖相交的两段的那个点上放炸弹,就相当于是横着这一段的点编号向竖着的点连向一条边,所以我们将点编号之后,对于空着点横连竖,然后跑匈牙利就可以了。
肆、代码
using namespace Elaina;
const int maxn=50;
char maze[maxn+5][maxn+5];
int idx[maxn+5][maxn+5],idy[maxn+5][maxn+5];
int n,m;
inline void input(){
n=readin(1),m=readin(1);
rep(i,1,n)scanf("%s",maze[i]+1);
}
int cntx,cnty;
inline void getid(){
cntx=1;
rep(i,1,n){
int j=1;
while(j<=m){
if(maze[i][j]=='#'){++j;continue;}
while(j<=m && maze[i][j]!='#')idx[i][j++]=cntx;
++cntx;
}
}
cnty=1;
rep(j,1,m){
int i=1;
while(i<=n){
if(maze[i][j]=='#'){++i;continue;}
while(i<=n && maze[i][j]!='#')idy[i++][j]=cnty;
++cnty;
}
}
}
const int maxnode=2500;
struct edge{int to,nxt;}e[maxnode*maxnode+5];
int tail[maxnode+5],ecnt;
inline void add_edge(const int u,const int v){
e[++ecnt]=edge{v,tail[u]};tail[u]=ecnt;
}
int bu[maxnode+5][maxnode+5];
inline void buildg(){
rep(i,1,n)rep(j,1,m)if(maze[i][j]=='*'){
int u=idx[i][j],v=idy[i][j];
if(!bu[u][v])add_edge(u,v),bu[u][v]=1;
}
}
int vis[maxnode+5],match[maxnode+5];
int hungary(const int u){
for(int i=tail[u],v;i;i=e[i].nxt)if(!vis[v=e[i].to]){
vis[v]=1;
if(!match[v] || hungary(match[v])){
match[v]=u;
return 1;
}
}
return 0;
}
inline void solve(){
int ans=0;
rep(i,1,cntx){
memset(vis+1,0,cnty<<2);
if(hungary(i))++ans;
}
writc(ans,'\n');
}
signed main(){
input();
getid();
// puts("finished getid()");
// printf("cntx == %d, cnty == %d\n",cntx,cnty);
buildg();
// puts("finished build");
solve();
return 0;
}
伍、用到の一些小 \(\tt trick\)
考虑转换模型,对于这种有一个点完全将两段隔开,可以将两段分别看成两个点.