【博弈+二分图匹配】[NOI2011]兔兔与蛋蛋游戏

题目描述

Description

Input

输入的第一行包含两个正整数 n、m。
接下来 n行描述初始棋盘。其中第i 行包含 m个字符,每个字符都是大写英文字母"X"、大写英文字母"O"或点号"."之一,分别表示对应的棋盘格中有黑色棋子、有白色棋子和没有棋子。其中点号"."恰好出现一次。
接下来一行包含一个整数 k(1≤k≤1000) ,表示兔兔和蛋蛋各进行了k次操作。
接下来 2k行描述一局游戏的过程。其中第 2i – 1行是兔兔的第 i 次操作(编号为i的操作) ,第2i行是蛋蛋的第i次操作。每个操作使用两个整数x,y来描述,表示将第x行第y列中的棋子移进空格中。
输入保证整个棋盘中只有一个格子没有棋子, 游戏过程中兔兔和蛋蛋的每个操作都是合法的,且最后蛋蛋获胜。

Output

输出文件的第一行包含一个整数r,表示兔兔犯错误的总次数。
接下来r 行按递增的顺序给出兔兔“犯错误”的操作编号。其中第 i 行包含一个整数ai表示兔兔第i 个犯错误的操作是他在游戏中的第 ai次操作。
1 ≤n≤ 40, 1 ≤m≤ 40

Sample Input

样例一:

1 6

XO.OXO

1

1 2

1 1

样例二:

3 3

XOX

O.O

XOX

4

2 3

1 3

1 2

1 1

2 1

3 1

3 2

3 3

样例三:

4 4

OOXX

OXXO

OO.O

XXXO

2

3 2

2 2

1 2

1 3

Sample Output

样例一:

1

1

样例二:

0

样例三:

2

1

2

样例1对应图一中的游戏过程

样例2对应图三中的游戏过程

HINT

Source

Day2

分析

75分做法

我们把操作看做是空格在移动。

我们做这道题最先想到的就是,操作是否可能成环?

答案是否定的。我们假设环长为n,显然n为偶数,因为环中的操作总是成对出现的,即有向上就要有向下,有向左就有向右。假设操作为A1,A2,A3…An,显然操作A1和An移入空格的是同一个棋子,但是操作的人却不一样,显然是不可能的,所以操作不可能成环。

既然不能成环,而且数据还这么小,似乎搜索很好写。敲一发,75分到手。

满分做法

我们发现,移动路径上,相邻两个点的颜色总是不一样的,这然我们想到了二分图。

我们在相邻而且颜色不相同的两个点之间连边,由于空格能够走到白色格子,我们不妨把空格看做黑色。

先手在当前点能够获胜的条件是从当前点出发,能够找到一条路径长度是奇数而且先手一定能够使路径长度为奇数。

由于是二分图,那我们求个最大匹配试试。

我们发现,匈牙利算法增广时的交错轨不就是一条长度为奇数的路径吗。

那么我们来思考一下两种情况。

  1. 如果当前点不一定最大匹配上,那么它的邻接点一定在最大匹配上。 我们假设它的邻接点不一定在最大匹配上:

    1. 存在邻接点不在时,当前点也不在。那么久可以增广了,和假设是最大匹配不符。
    2. 如果邻接点和当前点一定有一个在,那么邻接点和当前点一定还通过一个有偶数条边的交错轨相连,加上这条边就是一个奇环了,不是二分图。
  2. 如果当前点一定在最大匹配上,则一定获胜

    首先,当前点一定在一条增广路径上,即在一条交错轨上。这条交错轨一定是奇数的,而且被这个点分成了一边是奇数,一边是偶数。但是奇数那一边也有可能有一条偶数的交错轨,即可能有岔路。

    我们来分析一下,岔路可能有两种情况。

    显然,这种情况你自己不去走那边就好了。

    1. 出现在别人的节点

      如果朝奇数条交错轨的方向走,自己的节点到别人的节点这条边应该在最大匹配中(图中蓝色的边),但是这条边显然可以被橙色的边代替,和当前点一定在最大匹配上的假设不符。

      所以,最终一定走的是奇数条边,使后手无法行动。 也可以这么看,先手一定可以有边可走(沿着匹配边走),但是后手不一定有边可走。

由此我们得出结论,如果先手所在的点在一定在最大匹配上,则先手有必胜策略,否则,后手有必胜策略。

每次操作时,我们就检查当前空格所在位置是不是一定在最大匹配上,然后将这个位置删掉,将空格的位置设置为这次操作的位置。

那么我们怎么看当前点是不是一定在最大匹配上呢?

  • 如果当前点本来就没有匹配的点,显然不在。
  • 如果当前点所匹配的点在删去当前点后,能够继续增广,就说明当前点不一定在最大匹配上,否则一定在。

代码

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
#define MAXN 40
#define MAXM 40
#define MAXK 1000
queue<int>q;
int n,m,cx[MAXN*MAXM+10],dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}},bx,by,cnt,ans[MAXK+10],k;
char s[MAXN+10][MAXN+10];
bool vis[MAXN*MAXM+10],ban[MAXN*MAXM+10];
struct node{
    int v;
    node *next;
}*adj[MAXN*MAXN+10],edge[MAXN*MAXN*4+10],*ecnt=edge;
inline void addedge(int u,int v){
    node *p=++ecnt;
    p->v=v;
    p->next=adj[u];
    adj[u]=p;
}
void Read(int &x){
    static char c;
    while(c=getchar(),c!=EOF)
        if(c>=‘0‘&&c<=‘9‘){
            x=c-‘0‘;
            while(c=getchar(),c>=‘0‘&&c<=‘9‘)
                x=x*10+c-‘0‘;
            ungetc(c,stdin);
            return;
        }
}
void read(){
    Read(n),Read(m);
    int i,j;
    for(i=1;i<=n;i++)
        scanf("%s",s[i]+1);
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            if(s[i][j]==‘O‘)
                s[i][j]=0;
            else if(s[i][j]==‘X‘)
                s[i][j]=1;
            else
                s[i][j]=1,bx=i,by=j;
}
inline int Get_id(int i,int j){
    return (i-1)*m+j;
}
void bfs(){
    q.push(Get_id(bx,by));
    int u,x,y,tx,ty,v,d;
    while(!q.empty()){
        u=q.front();
        q.pop();
        x=(u+m-1)/m;
        y=u-(x-1)*m;
        for(d=0;d<4;d++){
            tx=x+dir[d][0];
            ty=y+dir[d][1];
            if(tx&&ty&&tx<=n&&ty<=m&&s[x][y]^s[tx][ty]){
                v=Get_id(tx,ty);
                addedge(u,v);
                if(!vis[v]){
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
}
void print(){
    printf("%d\n",cnt);
    int i;
    for(i=1;i<=cnt;i++)
        printf("%d\n",ans[i]);
}
bool dfs(int u){
    for(node *p=adj[u];p;p=p->next){
        if(!vis[p->v]&&!ban[p->v]){
            vis[p->v]=1;
            if(!cx[p->v]||dfs(cx[p->v])){
                cx[p->v]=u;
                cx[u]=p->v;
                return 1;
            }
        }
    }
    return 0;
}
void solve(){
    int i,u,mt;
    for(i=n*m;i;i--){
        if(!cx[i]){
            memset(vis,0,sizeof vis);
            dfs(i);
        }
    }
    bool r1,r2;
    Read(k);
    for(i=1;i<=k;i++){
        u=Get_id(bx,by);
        ban[u]=1;
        if(cx[u]){
            mt=cx[u];
            cx[u]=cx[mt]=0;
            memset(vis,0,sizeof vis);
            r1=!dfs(mt);
        }
        else
            r1=0;
        Read(bx),Read(by);
        u=Get_id(bx,by);
        ban[u]=1;
        if(cx[u]){
            mt=cx[u];
            cx[u]=cx[mt]=0;
            memset(vis,0,sizeof vis);
            r2=!dfs(mt);
        }
        else
            r2=0;
        if(r1&&r2)
            ans[++cnt]=i;
        Read(bx),Read(by);
    }
}
int main()
{
    read();
    bfs();
    solve();
    print();
}
时间: 2024-10-10 03:57:07

【博弈+二分图匹配】[NOI2011]兔兔与蛋蛋游戏的相关文章

bzoj 2437 [Noi2011]兔兔与蛋蛋 [二分图匹配]

描述 这些天,兔兔和蛋蛋喜欢上了一种新的棋类游戏. 这个游戏是在一个 n 行 m 列的棋盘上进行的.游戏开始之前,棋盘上有一 个格子是空的,其它的格子中都放置了一枚棋子,棋子或者是黑色,或者是白色. 每一局游戏总是兔兔先操作,之后双方轮流操作,具体操作为: 兔兔每次操作时,选择一枚与空格相邻的白色棋子,将它移进空格. 蛋蛋每次操作时,选择一枚与空格相邻的黑色棋子,将它移进空格. 第一个不能按照规则操作的人输掉游戏. 最近兔兔总是输掉游戏,而且蛋蛋格外嚣张,于是兔兔想请她的好朋友-- 你--来帮助

博弈论(二分图匹配):NOI 2011 兔兔与蛋蛋游戏

Description Input 输入的第一行包含两个正整数 n.m. 接下来 n行描述初始棋盘.其中第i 行包含 m个字符,每个字符都是大写英文字母"X".大写英文字母"O"或点号"."之一,分别表示对应的棋盘格中有黑色棋子.有白色棋子和没有棋子.其中点号"."恰好出现一次. 接下来一行包含一个整数 k(1≤k≤1000) ,表示兔兔和蛋蛋各进行了k次操作. 接下来 2k行描述一局游戏的过程.其中第 2i – 1行是兔兔的

NOI2011 兔兔与蛋蛋游戏

http://www.lydsy.com/JudgeOnline/problem.php?id=2437 这道题真是极好的. 75分做法: 搜索. 出题人真的挺良心的,前15个数据点的范围都很小,可以直接搜索. #include<cstdio> #include<cstdlib> #include<iostream> #include<fstream> #include<algorithm> #include<cstring> #in

[匈牙利算法][博弈] Luogu P1971 兔兔与蛋蛋

题目描述 这些天,兔兔和蛋蛋喜欢上了一种新的棋类游戏. 这个游戏是在一个n行m列的棋盘上进行的.游戏开始之前,棋盘上有一个格子是空的,其它的格子中都放置了一枚棋子,棋子或者是黑色,或者是白色. 每一局游戏总是兔兔先操作,之后双方轮流操作,具体操作为: 兔兔每次操作时,选择一枚与空格相邻的白色棋子,将它移进空格. 蛋蛋每次操作时,选择一枚与空格相邻的黑色棋子,将它移进空格. 第一个不能按照规则操作的人输掉游戏.为了描述方便,下面将操作“将第x行第y列中的棋子移进空格中”记为M(x,y). 例如下面

POJ2584 T-Shirt Gumbo 二分图匹配(网络流)

1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 const int inf=0x3f3f3f3f; 6 const int sink=30; 7 8 struct Edge 9 { 10 int to; 11 int next; 12 int capacity; 13 14 void assign(int t,int n,int c) 15 { 16 to=t; next=n; ca

棋盘游戏(二分图匹配)

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1281 棋盘游戏 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 3200    Accepted Submission(s): 1897 Problem Description 小 希和Gardon在玩一个游戏:对一个N*M的棋盘,在格子里放

BZOJ 1854 游戏(二分图匹配或并查集)

此题的二分图匹配做法很容易想,就是把属性当做s集,武器当做t集,如果该武器拥有该武器则连一条边. 那么答案就是求该二分图的最大前i个匹配.将匈牙利算法改一改,当前找不到增广路就break. 但是过这个题需要常数优化,不能每次都fillchar一遍used数组.可以用队列将使用的used点加入,然后需要初始化的时候弹出即可. # include <cstdio> # include <cstring> # include <cstdlib> # include <i

HDU 3081:Marriage Match II(二分图匹配+并查集)

http://acm.hdu.edu.cn/showproblem.php?pid=3081 题意:有n个男生n个女生,他们只有没有争吵或者女生a与男生A没有争吵,且女生b与女生a是朋友,因此女生b也可以和男生A过家家(具有传递性).给出m个关系,代表女生a和男生b没有争吵过.给出k个关系,代表女生a与女生b是好朋友.每一轮过家家之后,女生只能选择可以选择并且没选过的男生过家家,问游戏能进行几轮. 思路:因为n<=100,因此支持O(n^3)的算法,挺容易想到是一个二分图匹配的.(出现在我的网络

11082 - Matrix Decompressing (网络流建模|二分图匹配)

该题是一道经典的二分图匹配的题目 .现在终于有点明白什么是二分图匹配了,其实说白了就是依赖于最大流算法之上的一种解决特定问题的算法 . 所谓二分图,就是我们假定有两个集合A和B,每个集合中有若干元素(点),其中源点与A相连,汇点与B相连,并且他们的总容量决定了最终答案的上限,所以一定要维护好 . 然后由A中的点向B中的点连线,他们之间也有一定的容量制约关系(具体看题目中的边权值限制).这样就可以求出最大流量匹配了. 有时我们要求完美匹配,即所有流入的量等于流出的量  . 该题构思极其巧妙,因为我