15743 兔兔与蛋蛋游戏

描述

这些天,兔兔和蛋蛋喜欢上了一种新的棋类游戏。

这个游戏是在一个 n 行 m 列的棋盘上进行的。游戏开始之前,棋盘上有一个格子是空的,其它的格子中都放置了一枚棋子,棋子或者是黑色,或者是白色。

每一局游戏总是兔兔先操作,之后双方轮流操作,具体操作为:

  • 兔兔每次操作时,选择一枚与空格相邻的白色棋子,将它移进空格。
  • 蛋蛋每次操作时,选择一枚与空格相邻的黑色棋子,将它移进空格。

第一个不能按照规则操作的人输掉游戏。为了描述方便,下面将操作“将第x行第y列中的棋子移进空格中”记为 M(x,y)。

例如下面是三个游戏的例子。

image.png

image.png

image.png

最近兔兔总是输掉游戏,而且蛋蛋格外嚣张,于是兔兔想请她的好朋友——你——来帮助她。她带来了一局输给蛋蛋的游戏的实录,请你指出这一局游戏中所有她“犯错误”的地方。

注意:

  • 两个格子相邻当且仅当它们有一条公共边。
  • 兔兔的操作是“犯错误”的,当且仅当,在这次操作前兔兔有必胜策略,而这次操作后蛋蛋有必胜策略。

输入描述

输入的第一行包含两个正整数 n,m。

接下来 n 行描述初始棋盘。其中第 i 行包含 m 个字符,每个字符都是大写英文字母 X、大写英文字母 O 或点号 . 之一,分别表示对应的棋盘格中有黑色棋子、有白色棋子和没有棋子。其中点号 . 恰好出现一次。

接下来一行包含一个整数 k(1≤k≤1000) ,表示兔兔和蛋蛋各进行了 k 次操作。

接下来 2k 行描述一局游戏的过程。其中第 2i−1 行是兔兔的第 i 次操作(编号为 i 的操作) ,第 2i 行是蛋蛋的第 i 次操作。每个操作使用两个整数 x,y 来描述,表示将第 x 行第 y 列中的棋子移进空格中。

输入保证整个棋盘中只有一个格子没有棋子, 游戏过程中兔兔和蛋蛋的每个操作都是合法的,且最后蛋蛋获胜。

输出描述

输出文件的第一行包含一个整数 r,表示兔兔犯错误的总次数。

接下来 r 行按递增的顺序给出兔兔“犯错误”的操作编号。其中第 i 行包含一个整数 ai​ 表示兔兔第 i 个犯错误的操作是他在游戏中的第 ai​ 次操作。

样例输入 1 

1 6 
XO.OXO 
1 
1 2 
1 1 

样例输出 1 

1
1

样例输入 2 

3 3 
XOX 
O.O 
XOX 
4 
2 3 
1 3 
1 2 
1 1 
2 1 
3 1 
3 2 
3 3 

样例输出 2 

0

样例输入 3 

4 4 
OOXX 
OXXO 
OO.O 
XXXO 
2 
3 2 
2 2 
1 2 
1 3 

样例输出 3 

2
1
2

提示

数据范围与提示

对于 100% 的数据,1≤n≤40,1≤m≤40,1≤k≤1000。

测试点编号nm
1,2n=11≤m≤20
3n=3m=4
4,5n=4m=4
6,7n=4m=5
8n=3m=7
9∼14n=21≤m≤40
15,161≤n≤161≤m≤16
17∼201≤n≤401≤m≤40
#include <bits/stdc++.h>
using namespace std;
const int N = 4005;
int n, m, k, cnt = 0, fir[N], nxt[N << 1], to[N << 1], vis[N], cxy[N], nx = 0, p[N][N], sx, sy, cans = 0, ans[N], win[N], del[N];
char a[N][N];
void ade(int u, int v) {
    cnt++, nxt[cnt] = fir[u], fir[u] = cnt, to[cnt] = v;
    cnt++, nxt[cnt] = fir[v], fir[v] = cnt, to[cnt] = u;
}
int dfs(int r) {
    vis[r] = 1;
    for (int i = fir[r]; i; i = nxt[i])
        if (!vis[to[i]] && !del[to[i]]) {//需判断邻接点是否被删除
            vis[to[i]] = 1;
            if (!cxy[to[i]] || dfs(cxy[to[i]])) {
                cxy[cxy[r]] = 0, cxy[to[i]] = r, cxy[r] = to[i];
                return 1;
            }
        }
    return 0;
}
void match() {
    for (int i = 1; i <= nx; i++)
        if (!cxy[i])
            memset(vis, 0, sizeof(vis)), dfs(i);
}
void getp() {
    int tmp = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            if (a[i][j] == 'O')
                p[i][j] = ++tmp;
    nx = tmp;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            if (a[i][j] == 'X') p[i][j] = ++tmp;
            else if (a[i][j] == '.') p[i][j] = ++tmp, sx = i, sy = j;
}
int dx[5] = {0, 1, 0, -1};
int dy[5] = {1, 0, -1, 0};
void edge(int x, int y) {
    for (int i = 0; i < 4; i++) {
        int xx = x + dx[i], yy = y + dy[i];
        if (xx >= 1 && xx <= n && yy >= 1 && yy <= m && p[xx][yy] > nx) ade(p[x][y], p[xx][yy]);
    }
}
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%s", a[i] + 1);
    getp();//重新标号,白点标 1~nx,剩下的 nx+1~n 依次标号
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            if (a[i][j] == 'O')
                edge(i, j);//建图
    match();//先来一发最大匹配
    scanf("%d", &k);
    for (int t = 1; t <= k * 2; t++) {
        int x = p[sx][sy];
        scanf("%d%d", &sx, &sy);//注意要在continue前输入
        del[x] = 1;//直接删除
        if (!cxy[x]) continue;
        int tmp = cxy[x];
        cxy[x] = cxy[tmp] = 0, memset(vis, 0, sizeof(vis));
        if (!dfs(tmp)) win[t] = 1;//当前状态下(未操作)先手必胜
    }
    for (int i = 2; i <= k * 2; i += 2)
        if (win[i] == 1 && win[i - 1] == 1)
            ans[++cans] = i / 2;
    printf("%d\n", cans);
    for (int i = 1; i <= cans; i++) printf("%d\n", ans[i]);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值