[JZOJ5046]机器人游戏

题目描述

小A和小B在一个 R S列的棋盘上玩游戏,棋盘上的每一个棋格都有一个方向标记(上、下、左或右)。游戏按如下方式进行:
小A先将 K 个棋格涂上黑色(初始为白色),并且他不能涂黑最后一列的棋格;随后,小B在第一列的任意一个棋格上放一个小机器人;此时,小机器人将会不停地沿着他所在的棋格所指示的方向走到一个相邻的棋格,直到他到达最后一列的棋格,游戏结束。
游戏胜负规则如下:
●如果小机器人最终到达最后一列,且在游戏过程中经过了恰好一个黑色棋格(包括小机器人开始的棋格),那么小A获得胜利;如果小机器人经过了零个或大于一个黑色格子,那么小B获得胜利。
●如果机器人永远无法停下来,那么小A胜利。
题目保证每个棋格上的方向标记不会让小机器人走到棋盘外。
棋盘示意图如下:
棋盘
现在小A想知道,他是否能找到一种涂黑格子的方案,使得不论小B如何放置游戏开始时的小机器人,他都能取得游戏的胜利。

1R×S106,1K50


题目分析

排除掉不会走到的格子以及会死循环的格子,所有格子都构成了一个森林。
现在的问题是要涂黑 K 个点,使得每一个对应于第一列的点都有且仅有一个被涂黑的祖先。
这个问题存在树形依赖关系,可以使用很套路的DFS序dp:
fi,j表示做到DFS序的第 i 个点,已经涂黑了j个点,是否能使前 i1 个点都合法。瞎讨论转移一下就好了。
注意因为那些不在森林上的点也可以涂黑,最后我们把它们的贡献考虑进去就好了。
记录方案的话,因为这题卡空间,所以我们记录涂黑点数直接用一个bool记录是否新涂黑了点就好了。
时间复杂度 O(RSK)


代码实现

#include <iostream>
#include <cstdio>
#include <cctype>

using namespace std;

const int N=1000050;
const int MAXK=55;

int vis[N],fa[N],DFN[N],last[N],tov[N],nxt[N],size[N],p[N];
int black[MAXK][2];
bool dk[N][MAXK];
bool f[N][MAXK];
int g[N][MAXK];
char MAP[N];
int row,col,K,tot,idx,REM,bs;
bool win;

int getid(int x,int y){return x*col+y;}
void getxy(int id,int &x,int &y){x=id/col,y=id%col;}

void insert(int x,int y){tov[++tot]=y,nxt[tot]=last[x],last[x]=tot;}

bool simulation(int x,int y,int cnt)
{
    int id=getid(x,y);
    if (vis[id]==cnt) return 0;
    if (vis[id]) return 1;
    vis[id]=cnt;
    if (MAP[id]=='L') --y;
    if (MAP[id]=='R') ++y;
    if (MAP[id]=='U') --x;
    if (MAP[id]=='D') ++x;
    if (y+1==col) return 1;
    int id_=getid(x,y);
    return fa[id]=id_,simulation(x,y,cnt);
}

void dfs(int x)
{
    p[DFN[x]=++idx]=x,size[x]=1;
    for (int i=last[x],y;i;i=nxt[i]) if ((y=tov[i])!=fa[x]) dfs(y),size[x]+=size[y];
}

void dp()
{
    win=0,f[1][0]=1;
    for (int x=1;x<=idx;++x)
        for (int k=0;k<=K;++k)
            if (f[x][k])
            {
                if ((p[x]%col)&&!f[x+1][k]) f[x+1][k]=1,g[x+1][k]=x,dk[x+1][k]=0;
                if (k<K&&!f[x+size[p[x]]][k+1]) f[x+size[p[x]]][k+1]=1,g[x+size[p[x]]][k+1]=x,dk[x+size[p[x]]][k+1]=1;
            }
    for (int k=0;k<=K;++k)
        if (f[idx+1][k]&&K-REM<=k)
        {
            win=1;
            for (int id=g[idx+1][k],k_=k-dk[idx+1][k],k__=k;id!=0;k__=k_,k_-=dk[id][k_],id=g[id][k__])
                if (k_!=k__) ++bs,getxy(p[id],black[bs][0],black[bs][1]),++black[bs][0],++black[bs][1];
            REM=K-k;
            for (int x=0;x<row*col&&bs<K;++x) if (!DFN[x]&&(x%col!=col-1)) ++bs,getxy(x,black[bs][0],black[bs][1]),++black[bs][0],++black[bs][1];
            break;
        }
}

int main()
{
    freopen("robots.in","r",stdin),freopen("robots.out","w",stdout);
    scanf("%d%d%d",&row,&col,&K);
    for (int i=0;i<row;++i)
        for (int j=0;j<col;++j)
        {
            char ch=getchar();
            while (!isalpha(ch)) ch=getchar();
            MAP[getid(i,j)]=ch;
        }
    win=0;
    if (col>1&&row*(col-1)>=K)
    {
        for (int x=0;x<row*col;++x) fa[x]=-1;
        for (int i=0;i<row;++i) simulation(i,0,i+1);
        REM=idx=0;
        for (int x=0;x<row*col;++x) if (fa[x]!=-1) insert(fa[x],x);
        for (int x=0;x<row*col;++x) if (vis[x]&&fa[x]==-1) dfs(x);
        REM=row*(col-1)-idx,dp();
    };
    if (!win) printf("-1\n");
    else for (int i=1;i<=K;++i) printf("%d %d\n",black[i][0],black[i][1]);
    fclose(stdin),fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值