• 题解 P4055 【[JSOI2009]游戏】


    题目链接

    Solution [JSOI2009]游戏

    题目大意:给定一个 (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
    ",i,j);
    	return 0;
    }
    
  • 相关阅读:
    java中main函数怎么调用外部非static方法
    java连接mysql数据库 三 实现增删改查操作
    JAVA使用JDBC连接MySQL数据库 二
    try 与catch的作用
    JAVA使用JDBC连接MySQL数据库 一
    web后台多用户操作同一条数据同步问题
    shell的算术比较运算符和逻辑运算符
    Linux磁盘空间存满的处理方法
    Linux清空日志的三种方式
    Linux日志中查找关键字及其前后的信息实例方法
  • 原文地址:https://www.cnblogs.com/colazcy/p/14001686.html
Copyright © 2020-2023  润新知