HDU 3912 Turn Right + HDU 1254 推箱子 + HDU 1983 Kaitou Kid - The Phantom Thief (2)


http://acm.hdu.edu.cn/showproblem.php?pid=3912
这题应该算是个模拟题。题意大概是这样的:
一个n*m的网格,在第一行有个入口,第n行有个出口。然后人在里面行走必须满足如下条件:如果能向右走,就向右走,若不能向右走就向前走,如果不能向前走就向左走,如果不能向左走就像后走。题目中明确规定了可以保证从入口走到出口。问你的是:先从入口走到出口,再从出口走到入口,问能否遍历所有的网格。

因为题目所给的网格是有墙的。开始傻傻的我竟然用两个三维数组来存墙的位置,结果TLE,后来去食堂吃饭想起来可以用结构体存啊(too_young_too_simple),于是回来敲,结果TLE。次奥,当时就无语了。后来还是在巍神的启发下用了vis[4][N][N],这个数组的作用是存该点的可以走的方向 ===
我感受到了智商的压制 (大哭。。/(ㄒoㄒ)/~~

于是敲了代码,结果WA了。原因是有个地方没有注意到,我dfs的终止条件是当 (x==n&&y==ed)就退出,忽略了这种情况:
这里写图片描述
我的代码是只要到达这个网格就退出(红线),事实证明它还要往前走一步(黑线)。所以我在当先的地图上加了两个多余的网格。

反思: 谢谢巍神的 vis[4][N][N] 数组,以后长点心眼。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define  mset(x,y)  memset(x,y,sizeof(x));
using namespace std;
const int N = 500+10;
int n,m,st,ed;
bool Map[N][N];
int cnt;
bool vis[4][N][N];//奇妙的数组
int dir[4][2]={1,0,0,-1,-1,0,0,1};
bool check(int x,int y,int xx,int yy,int i)
{
    if(xx==0&&yy==st+1) return true;
    if(xx==n+1&&yy==ed+1) return true;
    if(xx<1||xx>n) return false;
    if(yy<1||yy>m) return false;
    if(!vis[i][x][y]) return false;//一句话判掉不符合情况的
    return true;
}
void work(int x,int y,int aim,int op)
{
    if(op==1&&x==n+1&&y==ed+1) return;//注意是 (n+1行
    if(op==2&&x==0&&y==st+1) return;//递归终止条件变了

    int tmp,xx,yy;
    for(int i=0;i<4;i++)
    {   //本题的模拟关键处
        if(i==0) tmp=(aim+1)%4;
        else if(i==1) tmp=aim;
        else if(i==2) tmp=((aim-1)%4+4)%4;
        else tmp=(aim+2)%4;

        xx=x+dir[tmp][0];
        yy=y+dir[tmp][1];
        if(!check(x,y,xx,yy,tmp)) continue;
        if(Map[xx][yy])  cnt--,Map[xx][yy]=false;
        break;
    }
    work(xx,yy,tmp,op);
    return;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        mset(vis,true);
        mset(Map,true);
        scanf("%d%d%d%d",&n,&m,&st,&ed);
        for(int i=1;i<=2*n-1;i++)
        {
            int x;
            if(i%2)
            {
                for(int j=1;j<m;j++)
                {
                    scanf("%d",&x);
                    if(x==1) vis[3][(i+1)/2][j]=vis[1][(i+1)/2][j+1]=false;//!!!
                }
            }
            else
            {
                for(int j=1;j<=m;j++)
                {
                    scanf("%d",&x);
                    if(x==1) vis[0][i/2][j]=vis[2][i/2+1][j]=false;//!!!
                }
            }
        }
        cnt=n*m;
        Map[1][st+1]=Map[0][st+1]=Map[n+1][ed+1]=false;
        cnt--;
        work(1,st+1,0,1);
        work(n,ed+1,2,2);
        puts(cnt==0?"YES":"NO");
    }
    return 0;
}

http://acm.hdu.edu.cn/showproblem.php?pid=1254

题意不用说,以前玩过的小游戏。
需要注意的地方:

  • 人可以走箱子的终点,也就是 3 这个位置
  • 判断箱子是否可以推的前提是 人是否能到达箱子的另外一个方向
  • 箱子要推的地方不能是墙,而且箱子可以经过一个地方两次
  • 标记的时候用vis[4][N][N]标记人的位置和推箱子的方向(因为箱子可以到达一个地方两次,显然标记箱子没用;标记人的地方和在这里人往哪个方向推箱子)
  • 推箱子的时候箱子前进一步但是人不会动
  • 还有就是要细心耐心的debug…
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N =  10;
int Map[N][N];
bool vis[4][N][N];
struct Node
{
    int posx,posy;//当前箱子的位置
    int xx,yy;//当前人的位置
    int cnt;
};
int n,m;
int dir[4][2]={0,1,1,0,0,-1,-1,0};
struct Node1
{
    int x;int y;
};
bool check(int x,int y)
{
    if(x<1||x>n) return false;
    if(y<1||y>m) return false;
    if(Map[x][y]==1) return false;
    return true;
}
bool judge(int x,int y,int aimx,int aimy,int posx,int posy)//判断人是否可以到达箱子的另一端
{
    bool vvis[N][N];
    memset(vvis,true,sizeof(vvis));
    Node1 now,next;
    queue<Node1> que;
    now.x=x;now.y=y;
    vvis[x][y]=false;
    que.push(now);
    while(!que.empty())
    {
        now=que.front(); que.pop();
        if(now.x==aimx &&now.y==aimy) return true;
        for(int i=0;i<4;i++)
        {
            next.x=now.x+dir[i][0];
            next.y=now.y+dir[i][1];
            if(!check(next.x,next.y)) continue;
            if(!vvis[next.x][next.y]) continue;
            if(next.x==posx&&next.y==posy) continue;
            vvis[next.x][next.y]=false;
            que.push(next);
        }
    }
    return false;
}
int bfs(int x1,int y1,int x2,int y2)
{
    Node now,next;
    queue<Node> que;
    now.posx=x1;now.posy=y1;
    now.xx=x2; now.yy=y2;
    now.cnt=0;
    que.push(now);
    while(!que.empty())
    {
        now=que.front();
        que.pop();
        if(Map[now.posx][now.posy]==3) return now.cnt;
        for(int i=0;i<4;i++)
        {
            int aimx=now.posx-dir[i][0];
            int aimy=now.posy-dir[i][1];
            if(!judge(now.xx,now.yy,aimx,aimy,now.posx,now.posy)) continue;
            if(!vis[i][aimx][aimy]) continue;
            next.posx=now.posx+dir[i][0];
            next.posy=now.posy+dir[i][1];
            if(!check(next.posx,next.posy)) continue;
            next.xx=aimx; next.yy=aimy;//更新人的位置
            vis[i][aimx][aimy]=false;
            next.cnt=now.cnt+1;
            que.push(next);
        }
    }
    return -1;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int posx=1,posy=1,xx=1,yy=1;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&Map[i][j]);
            if(Map[i][j]==2) posx=i,posy=j;
            if(Map[i][j]==4) xx=i,yy=j;
        }
        memset(vis,true,sizeof(vis));
        printf("%d\n",bfs(posx,posy,xx,yy));
    }
    return 0;
}

http://acm.hdu.edu.cn/showproblem.php?pid=1983
题意很简单,全是中文。
这题让我惊讶的地方就是 暴力啊暴力啊暴力啊,从未见过如此暴力的题目。
因为要封锁的地方绝对是<=4的,所以暴力从小到大枚举可能封锁的区域,直到满足情况为止。
暴力出奇迹:既然时间给的是5秒就不要怂,暴力搞起来,被吓到了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N =10;
char Map[N][N];
int n,m,sx,sy,k;
struct Node
{
    int x,y,cnt;
    int has;
};
int dir[4][2]={0,1,1,0,0,-1,-1,0};
bool bfs()
{
    bool vis[10][10][2];
    memset(vis,true,sizeof(vis));
    Node now,next;
    queue<Node> que;
    now.x=sx; now.y=sy;
    now.cnt=0;
    now.has=0;
    vis[now.x][now.y][0]=false;
    que.push(now);
    while(!que.empty())
    {
        now=que.front();
        que.pop();
        if( Map[now.x][now.y]=='E' && now.has &&now.cnt<=k ) return false;
        if(now.cnt>k) continue;

        for(int i=0;i<4;i++)
        {
            next.x=now.x+dir[i][0];
            next.y=now.y+dir[i][1];
            if(next.x<1||next.x>n||next.y<1||next.y>m) continue;
            if(!vis[next.x][next.y][now.has]||Map[next.x][next.y]=='#') continue;
            next.has=now.has;
            if(Map[next.x][next.y]=='J') next.has=1;
            vis[next.x][next.y][next.has]=false;
            next.cnt=now.cnt+1;
            que.push(next);
        }
    }
    return true;
}
bool dfs(int t)
{
    if(!t) return bfs();

    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            if(Map[i][j]=='#'||Map[i][j]=='E') continue;
            char op=Map[i][j];
            Map[i][j]='#';
            if(dfs(t-1)) return true;
            Map[i][j]=op;
        }
    return false;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
       scanf("%d %d %d",&n,&m,&k);
       for(int i=1;i<=n;i++)
          for(int j=1;j<=m;j++)
          {
              scanf(" %c",&Map[i][j]);
              if(Map[i][j]=='S') sx=i,sy=j;
          }
       bool flag=false;
       for(int i=0;i<4;i++)
       {
           if(dfs(i)) { flag=true;printf("%d\n",i);break;}
       }
       if(!flag) printf("4\n");
    }
    return 0;
}

That’s all :)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值