LOJ#6033. 「雅礼集训 2017 Day2」棋盘游戏 题解
把每个非 '#' 的格子当成点,共用边的格子所代表的点之间连一条边,不难发现这是个二分图。
不难发现这个二分图上的博弈,先手必胜当且仅当起始点一定会被包含在最大匹配中,否则后手只要一直走匹配边就赢了。
那么我们判断每个点是否可以成为这样的起始点即可。
求最大匹配的部分可以使用匈牙利算法也可以直接网络流,复杂度为 \(\Theta(n^2m^2)\) 或 \(\Theta((nm)^{1.5})\)
code :
#include <bits/stdc++.h>
using namespace std;
template <typename T> void read(T &x){
static char ch; x = 0,ch = getchar();
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - '0',ch = getchar();
}
inline void write(int x){if (x > 9) write(x/10); putchar(x%10+'0'); }
inline int Get(){
static char ch; ch = getchar();
while (ch != '.' && ch != '#') ch = getchar();
return ch == '.' ? 1 : 0;
}
const int N = 105,V = N * N;
int n,m,mp[N][N],id[N][N],cntv;
int G[V][4],deg[V];
inline void add(int x,int y){ G[x][deg[x]++] = y; G[y][deg[y]++] = x; }
int match[V]; bool vis[V];
inline bool dfs(int x){
if (vis[x]) return 0; vis[x] = 1;
for (int y,i = 0; i < deg[x]; ++i)
if (!match[y=G[x][i]] || dfs(match[y])){ match[y] = x,match[x] = y; return 1; }
return 0;
}
inline void dfs_ans(int x){
if (vis[x]) return; vis[x] = 1;
for (int y,i = 0; i < deg[x]; ++i) if (match[y=G[x][i]]) dfs_ans(match[y]);
}
int main(){
int i,j;
read(n),read(m);
for (i = 1; i <= n; ++i) for (j = 1; j <= m; ++j) if (mp[i][j] = Get()) id[i][j] = ++cntv;
for (i = 1; i <= n; ++i) for (j = 1; j <= m; ++j) if (mp[i][j] && ((i+j)&1)){
if (id[i-1][j]) add(id[i][j],id[i-1][j]);
if (id[i+1][j]) add(id[i][j],id[i+1][j]);
if (id[i][j-1]) add(id[i][j],id[i][j-1]);
if (id[i][j+1]) add(id[i][j],id[i][j+1]);
}
for (i = 1; i <= n; ++i) for (j = 1; j <= m; ++j) if (mp[i][j] && ((i+j)&1))
memset(vis,0,cntv+1),dfs(id[i][j]);
memset(vis,0,cntv+1);
for (i = 1; i <= cntv; ++i) if (!match[i]) dfs_ans(i);
int ans = 0;
for (i = 1; i <= n; ++i) for (j = 1; j <= m; ++j) if (id[i][j] && vis[id[i][j]]) ++ans;
write(ans),putchar('\n');
for (i = 1; i <= n; ++i) for (j = 1; j <= m; ++j) if (id[i][j] && vis[id[i][j]]) write(i),putchar(' '),write(j),putchar('\n');
return 0;
}