• 校内模拟赛 coin


    题意:

      n*m的棋盘,每个格子可能是反着的硬币,正着的硬币,没有硬币,每次可以选未选择的一行或者未选择的一列,将这一行/列的硬币取反。如果没有可选的或者硬币已经全部正面,那么游戏结束。

      最后一次操作的选手获得一分,如果最终棋盘上的硬币全是正面,那么双方都获得两分,问先手最多的多少分。

    分析:

      双方的最优策略一定是尽量使硬币全是正面,然后在考虑最后一次操作。

      如果局面不可能使硬币全是正面,那么输出(n+m)&1。考虑如何判断。

      如果(i,j)硬币,如果是正面,那么i->j连一条权值为0的边,否则连一条权值为1的。从一个点开始染色,使整张图满足边权等于两个点权的异或值。边权为0表示两个点要么同时选,要么同时不选,为1表示两个点只能选一个。

      如果可以使硬币全是正面,然后对于一个联通块,可能是两种方式涂色,即确定一个点的颜色后,整个联通块也确定了。记录两个方式涂色后1的个数。

      一共有三种可能,两种方式1的个数都是偶数,都是奇数,一个是偶数一个是奇数。那么偶偶与奇奇的没法改变,只有偶奇的根据第一个人选的这个联通块的颜色来确定。

      那么讨论一下即可。

      或者求sg值后异或起来。

    代码

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<cctype>
    #include<set>
    #include<queue>
    #include<vector>
    #include<map>
    #include<bitset>
    using namespace std;
    typedef long long LL;
    
    inline int read() {
        int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
        for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
    }
    
    const int N = 305;
    int col[N], e[N][N], n, m, c1, c2;
    vector<int> T[N];
    char s[N][N];
    
    bool dfs(int u) {
        col[u] ? c1 ++ : c2 ++;
        for (int i = 0; i < T[u].size(); ++i) {
            int v = T[u][i];
            if (col[v] == -1) {
                col[v] = col[u] ^ e[u][v];
                if (dfs(v)) return 1;
            }
            else {
                if (col[v] != col[u] ^ e[u][v]) return 1;
            }
        }
        return 0;
    }
    void solve() {
        n = read(), m = read();
        for (int i = 1; i <= n; ++i) scanf("%s", s[i] + 1);
        for (int i = 0; i <= 300; ++i) T[i].clear();
        memset(col, -1, sizeof(col)); memset(e, 0, sizeof(e));
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j) {
                if (s[i][j] == 'e') continue;
                T[i].push_back(j + n); T[j + n].push_back(i);
                if (s[i][j] == 'o') e[i][j + n] = e[j + n][i] = 0;
                else if (s[i][j] == 'x') e[i][j + n] = e[j + n][i] = 1;
            }
        int ans1 = 0, ans2 = 0;
        for (int i = 1; i <= n + m; ++i) {
            if (col[i] == -1) {
                col[i] = 1; 
                c1 = 0, c2 = 0;
                if (dfs(i)) { cout << ((n + m) & 1) << "
    "; return ; }
                c1 %= 2, c2 %= 2;
                if (c1 && c2) ans1 ++;
                else if (c1 ^ c2) ans2 ++;
            }
        }
        ans1 %= 2, ans2 %= 2;
        if (ans1 | ans2) puts("3");
        else puts("2");
        for (int i = 1; i <= n + m; ++i) T[i].clear();
    }
    int main() {
        for (int T = read(); T --; solve());
        return 0;
    }
  • 相关阅读:
    Android用Application设置全局变量以及使用
    Android Spinner
    margin和padding的区别
    Java中Calendar常用方法封装
    Java中Calendar的用法以及获取当前日期的后一天
    Oracle Exists用法
    (Oracle)rownum用法详解
    Android中GestureDetector.OnGestureListener
    Java文件路径详解[转载]
    Android利用ViewFlipper实现屏幕切换动画效果
  • 原文地址:https://www.cnblogs.com/mjtcn/p/10577304.html
Copyright © 2020-2023  润新知