zoukankan      html  css  js  c++  java
  • @bzoj


    @description@

    在N*M的迷宫中有一个棋子,小AA首先任意选择棋子放置的位置。然后,小YY和小AA轮流将棋子移动到相邻的格子里。
    游戏的规则规定,在一次游戏中,同一个格子不能进入两次,且不能将棋子移动到某些格子中去。
    当玩家无法继续移动棋子时,游戏结束,最后一个移动棋子的玩家赢得了游戏。

    例如下图所示的迷宫,迷宫中”。”表示棋子可以经过的格子,而”#”表示棋子不可以经过的格子:
    .##
    ...
    #.#
    若小AA将棋子放置在(1,1),则小AA则无论如何都无法赢得游戏。
    而若小AA将棋子放置在(3,2)或(2,3),则小AA能够赢得游戏。例如,小AA将棋子放置在(3,2),小YY只能将它移动到(2,2),此时小AA再将棋子移动到(2,3),就赢得了游戏。

    小AA和小YY都是绝顶聪明的小朋友,且从不失误。小AA到底能不能赢得这场游戏?

    Input
    输入数据首先输入两个整数N,M,表示了迷宫的边长。 接下来N行,每行M个字符,描述了迷宫。

    Output
    若小AA能够赢得游戏,则输出一行"WIN",然后输出所有可以赢得游戏的起始位置,按行优先顺序输出 每行一个,否则输出一行"LOSE"(不包含引号)。

    Sample Input
    3 3
    .##
    ...
    #.#
    Sample Output
    WIN
    2 3
    3 2
    HINT
    对于100%的数据,有1≤n,m≤100。

    @solution@

    经典题目.jpg。

    首先棋盘是一个经典的二分图,通过黑白染色把二分图搞出来过后,相当于是问从每一个结点出发,后手是否有必胜策略。
    假如从左边出发,先手一定是左 -> 右,后手一定是右 -> 左。发现往返的这个过程好像有点像增广路。

    如果后手必胜,则最终的路径长度一定为偶数。
    联系到增广路的话,假如一开始的边为非匹配边,交替走匹配边与非匹配边,并且最终路径长度如果为奇数,则一定可以扩大匹配的数量。
    如果当前已经是最大匹配了,匹配数量不可能扩大,则可以反推出最终路径长度一定为偶数。
    如果一个点不在某一个最大匹配中,则它所连的全部边都为非匹配边。先手无论选择哪一条边,只要后手一直沿匹配边走,都是后手必胜。

    反过来,如果一个点在所有最大匹配中,如果先手选择一直走匹配边一定必胜。
    因为假如最后得到的路径长度为偶数,则可以将这条路径的匹配 -> 非匹配,非匹配 -> 匹配,得到的依然是最大匹配且起点不在最大匹配中,矛盾。
    所以最后得到的路径长度一定为奇数,也就是说先手必胜。

    @accepted code@

    #include <cstdio>
    #include <vector>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int MAXN = 100*100;
    const int dx[] = {1, -1, 0, 0};
    const int dy[] = {0, 0, 1, -1};
    typedef pair<int, int> pii;
    vector<pii>ans;
    void print() {
    	if( ans.empty() ) puts("LOSE");
    	else {
    		puts("WIN");
    		for(int i=0;i<(int)ans.size();i++)
    			printf("%d %d
    ", ans[i].first, ans[i].second);
    	}
    }
    int lnk[MAXN + 5]; bool vis[MAXN + 5];
    vector<int>G[MAXN + 5], v[2];
    void clear(int t) {
    	for(int i=0;i<v[t].size();i++)
    		vis[v[t][i]] = false;
    }
    bool dfs(int x) {
    	for(int i=0;i<G[x].size();i++) {
    		int t = G[x][i];
    		if( vis[t] ) continue;
    		vis[t] = true;
    		if( !lnk[t] || dfs(lnk[t]) ) {
    			lnk[t] = x, lnk[x] = t;
    			return true;
    		}
    	}
    	return false;
    }
    bool check(int x) {
    	for(int i=0;i<G[x].size();i++) {
    		int t = G[x][i];
    		if( vis[t] ) continue;
    		vis[t] = true;
    		if( !lnk[t] || check(lnk[t]) )
    			return true;
    	}
    	return false;
    }
    int id[100 + 5][100 + 5], cnt;
    char s[100 + 5][100 + 5];
    int main() {
    	int N, M; scanf("%d%d", &N, &M);
    	for(int i=1;i<=N;i++)
    		scanf("%s", s[i] + 1);
    	for(int i=1;i<=N;i++)
    		for(int j=1;j<=M;j++)
    			if( s[i][j] == '.' )
    				id[i][j] = (++cnt);
    	for(int i=1;i<=N;i++)
    		for(int j=1;j<=M;j++) {
    			if( s[i][j] == '.' ) {
    				for(int k=0;k<4;k++) {
    					int x = i + dx[k], y = j + dy[k];
    					if( x < 1 || y < 1 || x > N || y > M ) continue;
    					if( s[x][y] == '#' ) continue;
    					G[id[i][j]].push_back(id[x][y]);
    				}
    				v[(i+j)&1].push_back(id[i][j]);
    			}
    		}
    	for(int i=0;i<v[0].size();i++)
    		clear(1), dfs(v[0][i]);
    	for(int i=1;i<=N;i++)
    		for(int j=1;j<=M;j++)
    			if( s[i][j] == '.' ) {
    				if( !lnk[id[i][j]] )
    					ans.push_back(make_pair(i, j));
    				else {
    					clear((i+j)&1), vis[id[i][j]] = true;
    					if( check(lnk[id[i][j]]) )
    						ans.push_back(make_pair(i, j));
    				}
    			}
    	print();
    }
    

    @details@

    为了偷懒写了个每次删点重新增广的算法。。。
    其实比较快的应该是用网络流 + 连通性判一个点是否存在于所有最大匹配中。

  • 相关阅读:
    Mycat 安全设置
    基于 HA 机制的 Mycat 高可用
    mycat中间件进行MySQL数据表的水平拆分
    mycat中间件进行MySQL数据库的垂直拆分
    mycat中间件进行MySQL的数据读写分离
    dubbo环境搭建--Windows
    分布式应用架构的发展演变RPC
    Java新特性--方法引用
    Java新特性-stream流
    Java新特性-四大函数式接口
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11846709.html
Copyright © 2011-2022 走看看