题目链接

题目大意:给定一个 \(n\)\(m\) 列的棋盘,有些格子不能放棋子,\(A\) 选择一个位置放棋子,然后 \(B,A\) 轮流操作,每次可以将棋子移动到相邻位置,同一个位置不能被放两次,求放在哪些位置 \(A\) 先手必胜

二分图博弈,网络流


分析:

棋盘上棋子相邻移动是一个比较经典的二分图模型,问题可以变化成一个二分图博弈问题。

如果这个二分图有完全匹配,那么先手必败。无论先手选哪个位置,后手只要沿着匹配边走就可以获胜。

反之,只要先手选非匹配点,就可以把后手逼入匹配点,转换为上一个问题从而先手必胜。

而如果对于所有匹配方案,一个点都是非匹配点,那么先手选这个位置必胜。

因此可以想到暴力算法,每次删一个点,看一下最大匹配是否保持不变。

每次暴力跑 \(Dinic\) 复杂度无法接受,我们初始跑一次 \(Dinic\) ,然后分类讨论。

  • 一个点是非匹配点,那么删去它之后最大匹配还是不变。
  • 一个点是匹配点,类似于匈牙利算法,我们找出一条交替路,这样交换路径上的边的匹配情况之后,最大匹配仍然不变,但是这个点变成了非匹配点从而可以被删除。

考虑残量网络,我们记源汇为 \(S,T\),分别与它们相连的点集为 \(X,Y\),那么答案为:

  • \(S\) 出发,走所有非满流边,能够走到的所有 \(X\) 点集的点。
  • \(T\) 出发,走所有满流边,能够走到的所有 \(Y\) 点集的点。

第一种情况,相当于找到了一条 非匹配-匹配--非匹配 \(\cdots\) 匹配路径,且路径起点是最初的非匹配点

第二种情况同理

代码请使用 C++17 编译

//-std=c++17
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
constexpr int maxn = 128,dx[] = {-1,1,0,0},dy[] = {0,0,-1,1};
namespace dinic{
	constexpr int maxn = ::maxn * ::maxn,maxm = maxn << 3;
    typedef int type;
    struct edge{int v;type cap;}edges[maxm];
    int head[maxn],nxt[maxm],tot = 1;
    inline void clear(){
		for(int i = 2;i <= tot;i++)
			head[edges[i].v] = 0,nxt[i] = 0;
		tot = 1;
	}
	inline void addedge(int u,int v,type d){
        edges[++tot] = edge{v,d};
        nxt[tot] = head[u];
        head[u] = tot;
        edges[++tot] = edge{u,0};
        nxt[tot] = head[v];
        head[v] = tot;
    }
    int d[maxn];
    inline bool bfs(int s,int t){
        memset(d,-1,sizeof(d));
        queue<int> q;
        q.push(s);d[s] = 0;
        while(!q.empty()){
            int u = q.front();q.pop();
            for(int i = head[u];i;i = nxt[i]){
                const edge &e = edges[i];
                if(e.cap && d[e.v] == -1){
                    d[e.v] = d[u] + 1;
                    q.push(e.v);
                }
            }
        }
        return d[t] != -1;
    }
    int cur[maxn];
    inline type dfs(int u,type a,int t){
        if(u == t || !a)return a;
        type res = 0,f;
        for(int &i = cur[u];i;i = nxt[i]){
            const edge &e = edges[i];
            if(d[u] + 1 == d[e.v] && (f = dfs(e.v,min(a,e.cap),t))){
                res += f;
                edges[i].cap -= f;
                edges[i ^ 1].cap += f;
                a -= f;
                if(!a)break;
            }
        }
        return res;
    }
    inline type maxflow(int s,int t){
        type res = 0;
        while(bfs(s,t)){
            memcpy(cur,head,sizeof(head));
            res += dfs(s,0x7fffffff,t);
        }
        return res;
    }
}
int n,m,ss,tt,ans[maxn][maxn],vis[maxn * maxn];
char mp[maxn][maxn];
pair<int,int> decode[maxn * maxn];
int encode[maxn][maxn];
inline void dfs(const int u,const int cp){
	if(vis[u])return;
	vis[u] = 1;
	const auto [x,y] = decode[u];
	if(((x + y) & 1) == cp)ans[x][y] = 1;
	for(int i = dinic::head[u];i;i = dinic::nxt[i]){
		const dinic::edge &e = dinic::edges[i];
		if(e.cap != cp || e.v == ss || e.v == tt)continue;
		dfs(e.v,cp);
	}
}
inline bool chk(int x,int y){return x >= 1 && x <= n && y >= 1 && y <= m;}
inline void build(){
	for(int i = 1;i <= n;i++)
		for(int j = 1;j <= m;j++)
			if(mp[i][j] == '.'){
				if((i + j) & 1)dinic::addedge(ss,encode[i][j],1);
				else dinic::addedge(encode[i][j],tt,1);
			}
	for(int x = 1;x <= n;x++)
		for(int y = 1;y <= m;y++)
			if((x + y) & 1 && mp[x][y] == '.')
				for(int i = 0;i < 4;i++){
					const int nx = x + dx[i],ny = y + dy[i];
					if(!chk(nx,ny) || mp[nx][ny] == '#')continue;
					dinic::addedge(encode[x][y],encode[nx][ny],1);
				}
}
int main(){
	scanf("%d %d",&n,&m);ss = n * m + 1,tt = ss + 1;
	int tot = 0;
	for(int i = 1;i <= n;i++)
		for(int j = 1;j <= m;j++)
			encode[i][j] = ++tot,decode[tot] = make_pair(i,j);
	for(int i = 1;i <= n;i++)scanf("%s",mp[i] + 1);
	int cnt[2];cnt[0] = cnt[1] = 0;
	for(int i = 1;i <= n;i++)
		for(int j = 1;j <= m;j++)
			if(mp[i][j] == '.')cnt[(i + j) & 1]++;
	build();
	int mx = dinic::maxflow(ss,tt);
	if(cnt[0] == cnt[1] && mx == cnt[0])return puts("LOSE"),0;
	dfs(ss,1);
	memset(vis,0,sizeof(vis));
	dfs(tt,0);
	puts("WIN");
	for(int i = 1;i <= n;i++)
		for(int j = 1;j <= m;j++)
			if(mp[i][j] == '.'&& ans[i][j])printf("%d %d\n",i,j);
	return 0;
}

相关文章:

  • 2021-05-21
  • 2021-09-29
  • 2021-06-30
  • 2021-07-03
  • 2022-02-16
猜你喜欢
  • 2021-10-07
  • 2021-10-01
  • 2022-01-16
  • 2021-08-07
  • 2021-05-25
  • 2021-07-11
相关资源
相似解决方案