• 【BZOJ2437】[Noi2011]兔兔与蛋蛋(博弈+二分图)


    传送门

    题意:
    给出一个(n*m)的棋盘,上面有若干个黑色棋子,若干个白色棋子,还有一个空格。
    每次先手选择一个空格,将它与相邻的某个白色棋子交换;后手则选择一个空格,与相邻的某个黑色棋子交换。
    最终不能移动的人输。

    现在给出一个棋盘以及游戏过程,最终的结果为先手输掉比赛。请你输出先手在这次比赛中失误了几次以及那几次的序号。

    思路:

    • 首先明确,先手失误:当且仅当在其操作前存在必胜策略,在其操作后后手存在必胜策略。
    • 然后考虑两个人的操作,将其等价于移动空格,先手只能移向白点,后手只能移向黑点。但这么考虑可能出现问题,因为实际过程中,黑点和白点的位置会发生改变,但其实可以证明:不会经过一个格子两次。这样的话直接移动空格的正确性就有保证。

    证明的话先考虑较小的情况发现不能形成环,然后行数加一或者列数加一改变了偶数个格子,也不影响。

    • 那么问题就转化为:从一个固定的黑点出发,形成一个黑白相间的路径,是否路径中某些点存在必胜策略。
    • 如果一个点存在必胜策略的话,那么从它出发肯定只用走奇数步即可,观察到这是一个二分图,也就是说,在一个二分图中,比如从左边出发,最终只能停留在右边;也就是说,左边出发的这个点一定是在最大匹配中的一个点,因为停留在左边意味着有另外一种匹配方案。
    • 问题就进一步转化为:给出一个二分图,判定对应的点是否一定在二分图中。那么这个直接将点和边拆掉暴力匈牙利就行。

    思路和game这个题有点类似,但又略有一点不同。
    注意一下,因为我们不能经过同一个点两次,也就是说如果当前走过了这个点,后面寻找增广路时也不能走这个点。不然思路就前后矛盾了。
    细节见代码:

    /*
     * Author:  heyuhhh
     * Created Time:  2019/11/7 10:52:33
     */
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    void pt() {std::cout << '
    '; }
    template<typename T, typename...Args>
    void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 45, M = 2605;
    
    int n, m;
    char s[N];
    int mp[N][N];
    vector <int> g[M];
    
    int id(int x, int y) {
        return (x - 1) * m + y;
    }
    
    bool ok(int x, int y) {
        return x >= 1 && x <= n && y >= 1 && y <= m && mp[x][y] == 0;
    }
    
    int T;
    int match[M], vis[M], ban[M];
    bool win[M];
    
    bool dfs(int x) {
        if(ban[x]) return false;   
        for(int i = 0; i < sz(g[x]); i++){
            int it = g[x][i];
            if(vis[it] != T && !ban[it]) {
                vis[it] = T;
                if(match[it] == -1 || dfs(match[it])) {
                    match[it] = x;
                    match[x] = it;
                    return true;
                }
            }   
        }
        return false;
    }
    
    void run(){
        cin >> n >> m;
        int X, Y;
        for(int i = 1; i <= n; i++) {
            cin >> (s + 1);
            for(int j = 1; j <= m; j++) {
                if(s[j] == 'X') mp[i][j] = 1;
                else if(s[j] == 'O') mp[i][j] = 0;
                else mp[i][j] = 1, X = i, Y = j;
            }
        }
        static int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= m; j++) if(mp[i][j]) {
                for(int k = 0; k < 4; k++) {
                    int nx = i + dx[k], ny = j + dy[k];
                    if(ok(nx, ny)) {
                        g[id(i, j)].push_back(id(nx, ny));
                        g[id(nx, ny)].push_back(id(i, j));
                    }
                }
            }
        }
        memset(match, -1, sizeof(match));
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= m; j++) if(mp[i][j]) {
                ++T; dfs(id(i, j));
            }
        }
        int k; cin >> k;
        for(int i = 1; i <= k + k; i++) {
            ban[id(X, Y)] = true;
            if(match[id(X, Y)] != -1) {
                int nw = match[id(X, Y)];
                match[id(X, Y)] = match[nw] = -1;
                ++T;
                win[i] = !dfs(nw);
            }   
            cin >> X >> Y;
        }
        vector <int> res;
        for(int i = 1; i <= k; i++) {
            if(win[2 * i - 1] && win[2 * i]) res.push_back(i);
        }
        cout << sz(res) << '
    ';
        for(int i = 0; i < sz(res); i++) cout << res[i] << '
    ';
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        run();
    	return 0;
    }
    
  • 相关阅读:
    委托
    Ajax TreeView绑定数据库(一)
    递归算法算出1,1,2,3,5.........(二)
    Ajax技术之深入浅出
    javasript读写xml
    递归算法求阶乘(一)
    关于.Net中的实际开发
    ASP.NET Form表单验证
    js 实现自动合并相同的行
    递归快速排序
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11811411.html
Copyright © 2020-2023  润新知