• 【bzoj2437】[Noi2011]兔兔与蛋蛋 二分图最大匹配+博弈论


    Description

    img

    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对应图三中的游戏过程

    Sol

    最近补了几发博弈论,但都是和sg函数有关的博弈,看到这题就懵了。

    后来才知道这个叫二分图博弈......

    具体地,这个操作相当于在移动这个空格,我们把空格变成黑色,那么就是按照黑白黑白黑白的顺序走,这好像有点二分图的感觉...

    然后我们把合法的点拉出来建四联通的二分图(到起点的曼哈顿距离和它的颜色正好匹配),之后这个博弈其实就是在二分图上跑增广路,而且经过的边不能再次经过,这样的话我们发现,一个点是必胜态当且仅当这个点一定是一个最大匹配包含的点,因为匹配边和连接匹配的边总个数是个奇数。。。只要一直顺着匹配连边走就能赢。。。(对手走的是连接两对匹配的边)否则的话一定是必败态(因为走一步一定走到了一个最大匹配上,假设走不到说明还有新的匹配,与最大匹配矛盾,不成立)。

    至于判断一个点是不是最大匹配的必需点,我们把它删掉,从它的原匹配点如果能找到最短路,说明它不是必需点。判断有没有做错是需要判断两个连续的状态是不是都是必胜态。。。

    然后好像就完了......

    Code

    #include <bits/stdc++.h>
    using namespace std;
    char s[45][45];vector<int>e[2500];
    int n,m,q,tot,Index,ans,a[45][45],sx,sy,u,v,vis[2500],match[2500],del[2500],win[2500];
    bool dfs(int x)
    {
    	for(int i=0;i<e[x].size();i++)
    	{
    		if(del[e[x][i]]||vis[e[x][i]]==Index) continue;
    		vis[e[x][i]]=Index;
    		if(!match[e[x][i]]||dfs(match[e[x][i]])){match[e[x][i]]=x,match[x]=e[x][i];return 1;}
    	}
    	return 0;
    }
    int main()
    {
    	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]=='.'){sx=i,sy=j,s[i][j]='X';break;}
    	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)
    		if((s[i][j]=='O'&&(abs(i-sx)+abs(j-sy))%2==1)||(s[i][j]=='X'&&(abs(i-sx)+abs(j-sy))%2==0)) a[i][j]=++tot;
    	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(a[i][j])
    	{
    		if(a[i-1][j]) e[a[i][j]].push_back(a[i-1][j]);if(a[i+1][j]) e[a[i][j]].push_back(a[i+1][j]);
    		if(a[i][j-1]) e[a[i][j]].push_back(a[i][j-1]);if(a[i][j+1]) e[a[i][j]].push_back(a[i][j+1]);
    	}
    	for(int i=1;i<=tot;i++) if(!match[i]) Index++,dfs(i);
    	scanf("%d",&q);q<<=1;
    	for(int i=1;i<=q;i++)
    	{
    		u=a[sx][sy];
    		if(match[u]) v=match[u],match[u]=match[v]=0,del[u]=1,Index++,win[i]=!dfs(v);
    		else del[u]=1,win[i]=0;
    		scanf("%d%d",&sx,&sy);
    	}
    	for(int i=1;i<=q;i++,i++) if(win[i]&&win[i+1]) ans++;
    	printf("%d
    ",ans);
    	for(int i=1;i<=q;i++,i++) if(win[i]&&win[i+1]) printf("%d
    ",(i+1)>>1);
    }
    
  • 相关阅读:
    python_24_test
    python_23_tuple
    python_22_enumerate
    python_20_列表
    python_21_copy
    python_19_编码解码
    python_18_三元运算
    python_16_自己建立模块
    关于主键(PRIMARY KEY)和自增(AUTO_INCREMENT)结合使用的知识点
    MySQL root用户忘记密码怎么办?修改密码方法:skip-grant-tables
  • 原文地址:https://www.cnblogs.com/CK6100LGEV2/p/9439780.html
Copyright © 2020-2023  润新知