【NOI2017】机器人游戏

6 篇文章 0 订阅

Description

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

Solution

首先可以发现,一些属于环或是不会被走到的点可以任意染黑,剩下的点以可到达的最后一列点为根,就有若干多棵树组成森林,可以视作在树上的背包。当前节点可以不染黑当且仅当它的所有叶子节点到它的路径上只有一个点被染黑,然后在最后把所有的树都整合在一起。

Code

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
#define fo(i,a,b) for(i=a;i<=b;i++)
#define rep(i,x) for(i=la[x];i;i=ne[i])
int F[4][2]={-1,0,1,0,0,-1,0,1};
const int N=1e6+5;
char ch;//0为未走过,1为环,2为可到达路径 
int map[N],p[N],la[N],da[N],ne[N],f[N][55],h[N/10][55],node[N],to[N/10][55],ans[N/10][2],g[5][55];
bool bz[N];
int n,m,o,i,j,k,w,sum,num,tot,LEFT;
void ins(int x,int y){da[++sum]=y,ne[sum]=la[x],la[x]=sum;}
int W(int x,int y){return x*m-m+y;}
int dfs(int x,int y){
    int w;
    if(y==m) return 2;
    w=W(x,y);
    if(p[w]) return p[w];
    if(bz[w]) return 1;
    bz[w]=true;p[w]=dfs(x+F[map[w]][0],y+F[map[w]][1]);
    if(p[w]==2) ins(W(x+F[map[w]][0],y+F[map[w]][1]),w);
    return p[w];
}
void dp(int x){
    int i,j,k;
    f[x][0]=0;
    if(x%m==1){
        f[x][1]=node[x]=1;return;
    }
    rep(k,x){
        dp(da[k]);
        node[x]++;
        for(i=o;i>=0;i--) if(f[x][i]==(node[x]-1)){
            fo(j,1,o) if(f[da[k]][j]==node[da[k]]) f[x][i+j]=node[x];
        }
    }
    f[x][1]=node[x];
}
void deal(int x,int tot){
    if(tot==1){
        printf("%d %d\n",x/m+1,x%m);
        LEFT--;
        return;
    }
    int i,j,k,A[5][2];
    node[x]=0;g[0][0]=x;
    rep(k,x){
        node[x]++;
        for(i=o;i>=0;i--) if(g[node[x]-1][i]==x){
            fo(j,1,o) if(f[da[k]][j]==node[da[k]])  g[node[x]][i+j]=x,to[node[x]][i+j]=j;
        }
        A[node[x]][0]=da[k];
    }
    k=tot;i=node[x];
    while(i>0&&tot>0){
        A[i][1]=to[i][k];k-=to[i][k];i--;
    }
    i=node[x];
    while(i>0){
        deal(A[i][0],A[i][1]);
        i--;
    }
}
int main(){
    freopen("robots.in","r",stdin);
    freopen("robots.out","w",stdout);

    scanf("%d%d%d\n",&n,&m,&o);
    LEFT=o;
    fo(i,1,n){
        fo(j,1,m){
            ch=getchar();
            if(ch=='U') map[W(i,j)]=0;
            if(ch=='D') map[W(i,j)]=1;
            if(ch=='L') map[W(i,j)]=2;
            if(ch=='R') map[W(i,j)]=3;
        }
        scanf("\n");
    }
    fo(i,1,n) if(!p[W(i,1)]) p[W(i,1)]=dfs(i,1);
    tot=n*(m-1);
    fo(i,1,n) fo(j,1,m-1) if(p[W(i,j)]==2) tot--;
    memset(f,255,sizeof(f));
    memset(h,255,sizeof(h));
    fo(i,0,min(tot,o)) h[0][i]=0;
    fo(i,1,n) if(la[W(i,m)]){
        dp(W(i,m-1));
        num++;w=W(i,m-1);
        for(j=o-1;j>=0;j--) if(h[num-1][j]>=0){
            fo(k,1,o-j) if(f[w][k]==node[w]) 
               h[num][j+k]=w,to[num][j+k]=k;
        }
    }
    if(h[num][o]<0){printf("-1\n");return 0;}
    tot=o;i=num;
    while(i>0&&tot>0){
        ans[i][0]=h[i][tot];ans[i][1]=to[i][tot];
        tot-=to[i][tot];i--;
    }
    i=num;
    while(i>0){
        deal(ans[i][0],ans[i][1]);
        i--;
    }
    fo(i,1,n) fo(j,1,m-1) if(LEFT&&p[W(i,j)]!=2) printf("%d %d\n",i,j),LEFT--;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值