bzoj1443 [JSOI2009]游戏Game 二分图 博弈

30 篇文章 0 订阅
9 篇文章 0 订阅

Description


小AA和小YY得到了《喜羊羊和灰太狼》的电影票,都很想去观看,但是电影票只有一张,于是他们用智力游戏决定胜负,赢得游戏的人可以获得电影票。
在N*M的迷宫中有一个棋子,小AA首先任意选择棋子放置的位置。然后,小YY和小AA轮流将棋子移动到相邻的格子里。游戏的规则规定,在一次游戏中,同一个格子不能进入两次,且不能将棋子移动到某些格子中去。当玩家无法继续移动棋子时,游戏结束,最后一个移动棋子的玩家赢得了游戏。
例如下图所示的迷宫,迷宫中”.”表示棋子可以经过的格子,而”#”表示棋子不可以经过的格子:

                             .##
                             ...
                             #.# 

若小AA将棋子放置在(1,1),则小 AA 则无论如何都无法赢得游戏。
而若小AA将棋子放置在(3,2)或(2,3),则小AA能够赢得游戏。例如,小AA将棋子放置在(3,2),小YY只能将它移动到(2,2),此时小AA再将棋子移动到(2,3),就赢得了游戏。
小AA和小YY都是绝顶聪明的小朋友,且从不失误。小AA到底能不能赢得这场游戏,从而得到珍贵的电影票呢?

对30%的数据,有1<=n,m<=5
对1000%的数据,有1<=n,m<=100

Solution


容易想到把矩阵黑白染色连边,这样就得到了一个二分图,求一遍最大匹配
对于先手而言,若二分图不存在完美匹配,则放在非匹配点一定是最优的。因为后手的第一步必定走到一个匹配点,先手只需要保证一直沿着匹配边走即可,因为不可能存在非匹配边、匹配边交叉出现的增广路,即先手总是能比后手多走一步(根据最大匹配的定义)
若二分图存在完美匹配,那么先手无论如何放置都会输,具体同理;-P
那么跑一遍网络流求完最大匹配后dfs求出非匹配点,这些都是可行的放置方案
大概就是这样,注意加当前弧优化
如果发现和洛谷某题解撞了的话辣个是我写的呀

Code

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
using std:: min;
std:: queue<int> que;
const int INF=0x3f3f3f3f;
const int L=205;
const int N=80005;
const int E=640005;
struct edge{int x,y,w,next;}e[E];
int dis[N],rc[L][L],vis[N],bel[N],chs[N];
int cur[N],ls[N],n,m,edCnt=1;
int dx[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
bool flag=false;
char str[L];
void addEdge(int x,int y,int w) {
    e[++edCnt]=(edge){x,y,w,ls[x]}; ls[x]=edCnt;
    e[++edCnt]=(edge){y,x,0,ls[y]}; ls[y]=edCnt;
    // printf("%d %d %d\n", x,y,w);
}
int get_pos(int x,int y) {return (x-1)*m+y;}
int bfs(int st,int ed) {
    while (!que.empty()) que.pop();
    que.push(st);
    rep(i,st,ed) dis[i]=-1; dis[st]=1;
    while (!que.empty()) {
        int now=que.front(); que.pop();
        for (int i=ls[now];i;i=e[i].next) {
            if (e[i].w>0&&dis[e[i].y]==-1) {
                dis[e[i].y]=dis[now]+1;
                if (e[i].y==ed) return 1;
                que.push(e[i].y);
            }
        }
    }
    return 0;
}
int find(int now,int ed,int mn) {
    if (now==ed||!mn) return mn;
    int ret=0;
    for (int &i=cur[now];i;i=e[i].next) {
        if (e[i].w>0&&dis[now]+1==dis[e[i].y]) {
            int d=find(e[i].y,ed,min(mn-ret,e[i].w));
            ret+=d; e[i].w-=d; e[i^1].w+=d;
            if (ret==mn) break;
        }
    }
    return ret;
}
int dinic(int st,int ed) {
    int ret=0;
    while (bfs(st,ed)) {
        rep(i,st,ed) cur[i]=ls[i];
        ret+=find(st,ed,INF);
    }
    return ret;
}
void dfs(int now,int lim) {
    if (vis[now]) return ;
    vis[now]=1;
    if (bel[now]==lim) {
        chs[now]=1;
        flag=true;
    }
    for (int i=ls[now];i;i=e[i].next) {
        if (e[i].w==lim) dfs(e[i].y,lim);
    }
}
int main(void) {
    scanf("%d%d",&n,&m);
    rep(i,1,n) {
        scanf("%s",str);
        rep(j,1,m) if (str[j-1]=='#') rc[i][j]=1;
    }
    rep(i,1,n) rep(j,1,m) {
        int now=get_pos(i,j);
        if (((i^j)&1)&&!rc[i][j]) {
            bel[now]=1;
            addEdge(0,now,1);
            rep(k,0,3) {
                int p=i+dx[k][0],q=j+dx[k][1];
                if (p>0&&p<=n&&q>0&&q<=m&&!rc[p][q]) addEdge(now,get_pos(p,q),1);
            }
        } else if (!((i^j)&1)&&!rc[i][j]) {
            addEdge(now,n*m+1,1);
        }
    }
    int mxFlow=dinic(0,n*m+1);
    dfs(0,1); fill(vis,0);
    dfs(n*m+1,0);
    if (flag) {
        puts("WIN");
        rep(i,1,n) rep(j,1,m) {
            if (chs[get_pos(i,j)]) {
                printf("%d %d\n", i,j);
            }
        }
    } else puts("LOSE");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值