• NOI2011 兔兔与蛋蛋(博弈论+二分图)


    题目

    分析:

    把移动的过程看做是中间空格在走,则空格一定是在黑格子与白格子间交替移动,这就变成了一个二分图的模型。

    通过题目可以得出两个性质

    1.棋盘上的每一点最多只被走一次

    2.如果兔兔将要移动的空格对应的点一定在二分图的最大匹配上,兔兔必胜。

    性质2的原因口胡:空格在最大匹配中,那么沿着匹配边走一个点,一定能走到下一个黑点,而黑点不一定能找到下一条边走到白点,兔兔必胜。

    如何判断那个点是否一定在最大匹配上呢?

    如果强制把那个点去掉,看是否能找到增广路,如果不能,就说明那个点一定在最大匹配上。

    两个性质的详细证明与分析

     但是题中要求的是错误步骤,应该怎么转换呢?

    把走的每一步转换成一个新的局面,然后通过上述性质去check此局面是先手必胜还是必败。

    #include<bits/stdc++.h>
    using namespace std;
    #define ri register int 
    #define N 2500
    #define nn 50
    int id[nn][nn],a[nn][nn],fl[N],to[N<<1],nex[N<<1],head[N],mat[N],vis[N],ans[N],win[N];
    int dx[5]={0,0,1,-1},dy[5]={1,-1,0,0},tot=0,T=0;
    char s[nn];
    int read()
    {
        int x=0,fl=1; char ch=getchar();
        while(ch>'9'||ch<'0') { if(ch=='-') fl=-1; ch=getchar(); }
        while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=getchar();
        return x;
    }
    void add(int a,int b) { to[++tot]=b; nex[tot]=head[a]; head[a]=tot; }
    bool dfs(int x)
    {
        if(fl[x]) return false;//被删除的点不会再被遍历到 
        for(ri i=head[x];i;i=nex[i]){
            int v=to[i];
            if(fl[v] || vis[v]==T) continue;
            vis[v]=T;
            if(!mat[v] || dfs(mat[v])){
                mat[x]=v;
                mat[v]=x;//最好记录两边 保证不重不漏 
                return true;
            }
        }
        return false;
    }
    int main()
    {
        //freopen("game.in","r",stdin);
        //freopen("game.out","w",stdout); 
        int n,m,x,y,cnt=0;
        n=read(); m=read();
        for(ri i=1;i<=n;++i) for(ri j=1;j<=m;++j) id[i][j]=++cnt;
        for(ri i=1;i<=n;++i){
            scanf("%s",s);
            for(ri j=1;j<=m;++j){
                a[i][j]=(s[j-1]=='O')?0:1;
                if(s[j-1]=='.') x=i,y=j;
            }
        }
        for(ri i=1;i<=n;++i)
         for(ri j=1;j<=m;++j)
          for(ri k=0;k<=3;++k){
              if(a[i][j]==0) continue;//保证是黑点向白点连边 
              int xx=i+dx[k],yy=j+dy[k];
              if(xx<1 || xx>n || yy<1 || yy>m || a[xx][yy]==1) continue;
              add(id[i][j],id[xx][yy]),add(id[xx][yy],id[i][j]);
        }
        for(ri i=1;i<=n;++i)
         for(ri j=1;j<=m;++j)
          if(a[i][j]==0){
            ++T;
            dfs(id[i][j]);
        }
        //题目中给了每一次的移动方式 其实可以转变成:每一次在走完之前步后的剩余棋盘下 判断是否可以先手必胜 
        int op=read();
        cnt=0;
        for(ri i=1;i<=op*2;++i){
            int now=id[x][y];
            fl[now]=1;//打标记说明这个点已经被去掉 
            if(mat[now]){
                int v=mat[now];
                mat[now]=mat[v]=0;
                ++T;//++T是为了判断有没有绕环 所以每dfs一次都要++T 
                win[i]= !dfs(v);//如果dfs成功了 说明可以找到一条不包含now的增广路 now是必败点 标记成 0 
            }
            //如果这个点直接没有在最大匹配中 就应该为必败点 
            x=read(),y=read();
        }
        //如果兔兔本来这步可以赢 就说明这一步它走错了 因为最后一定是兔兔必输状态 
        //根据题意判断两次 
        for(ri i=1;i<=op;++i) if( win[i<<1] && win[(i<<1)-1] ) ans[++cnt]=i;
        printf("%d
    ",cnt);
        for(ri i=1;i<=cnt;++i) printf("%d
    ",ans[i]);
        return 0; 
    }
    /*
    4 
    OOXX 
    OXXO 
    OO.O 
    XXXO
    
    2 
    2 
    2 
    3
    */
  • 相关阅读:
    一步一步学EF系列二【Fluent API的方式来处理实体与数据表之间的映射关系】
    EntityFramework 6 开篇
    DOS For循环
    mysql中查看所有表、表字段、表注释、字段注释
    灾备还原之gitlab
    JavaScript 实现继承的5种方式
    HBuilder完成webApp入门(5) 实例
    HBuilder完成webApp入门(4) 实例
    HBuilder完成webApp入门(3) 关于webview (转)
    HBuilder完成webApp入门(2)
  • 原文地址:https://www.cnblogs.com/mowanying/p/11508339.html
Copyright © 2020-2023  润新知