UVa 10047 - The Monocycle 优先队列+BFS

A monocycle is a cycle that runs on one wheel and the one we will be considering is a bit more special. It has a solid wheel colored with five different colors as shown in the figure:

The colored segments make equal angles (72o) at the center. A monocyclist rides this cycle on an $M \times N$ grid of square tiles. The tiles have such size that moving forward from the center of one tile to that of the next one makes the wheel rotate exactly 72o around its own center. The effect is shown in the above figure. When the wheel is at the center of square 1, the mid­point of the periphery of its blue segment is in touch with the ground. But when the wheel moves forward to the center of the next square (square 2) the mid­point of its white segment touches the ground.

Some of the squares of the grid are blocked and hence the cyclist cannot move to them. The cyclist starts from some square and tries to move to a target square in minimum amount of time. From any square either he moves forward to the next square or he remains in the same square but turns 90o left or right. Each of these actions requires exactly 1 second to execute. He always starts his ride facing north and with the mid­point of the green segment of his wheel touching the ground. In the target square, too, the green segment must be touching the ground but he does not care about the direction he will be facing.

Before he starts his ride, please help him find out whether the destination is reachable and if so the minimum amount of time he will require to reach it.


样例输入:

1 3
S#T
10 10
#S.......#
#..#.##.##
#.##.##.##
.#....##.#
##.##..#.#
#..#.##...
#......##.
..##.##...
#.###...#.
#.....###T
0 0

样例输出:

Case #1
destination not reachable
 
Case #2
minimum time = 49 sec


题意:一个骑独轮车的人,在图中从S走到T点,这个独轮车的轮子按圆平均分成5份,每份占72度,向邻近格子走一步颜色向前变化一格,开始的时候轮子蓝色着地,面朝北,走到T点的时候蓝色也必须着地,求在这种条件下走到T点的最短时间。行走的规定是:转向90度时间+1,转向180度时间+2,转动不改变颜色,向前走一步时间+1;只能上下左右(北南西东)走。


我的理解:一开始我想一个格子可以走多次,vis数组就开成int型,把每个格子的访问次数设置成1000,暴力跑,结果没跑出来,然后这种瞎暴力就不行。


然后思考这种方法有很多相同的状态,那么这些相同的状态有些什么特点,就是每个点朝其中一个方向走的时候,轮子底部的颜色是不同的,那么这个点在这个方向这种颜色的状态是不是只走一次就行了,后面重复走相同的状态只会比当前状态走的时间多(因为使用的优先队列)。所以在以前普通的二维vis数组后面多增加了2维,分别代表方向和颜色。


很重要的地方是怎样将新的点状态压入队列,以及什么样的点算新的点。一开始我想的是直接走,向前直接走一步,转方向的也是直接转了就往前走,这样是不对的,因为这样走去的下一个点可能有其他点也能到达并且使时间更小(特别向后转再走),这里wa几发后,然后就觉得可以更细一点,就是转动过后就压进队列,走动只能朝正对的方向走,这样后一个点的时间就只是前一个点的时间+1,可以保证到下一个点所用时间最少,然后写法可以自己研究下。


这道题感觉有点意思


///代码显示没高亮,可以看这里:点击打开链接

CODE:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <queue>

using namespace std;
const int maxn = 30;

struct node
{
    int x,y;    ///坐标
    int dis;    ///时间,习惯性写成dis
    int dir;    ///当前点的方向
    int blue;   ///当前点的颜色,0的时候是蓝色
    friend bool operator < (node a,node b)    ///优先队列重载
    {
        return a.dis > b.dis;
    }
};

int n,m;
char maze[maxn][maxn];        ///输入图
int vis[maxn][maxn][4][5];    ///标记,第3维是上下左右4个方向,第4维是5个颜色
int dir[4][2] = {{-1,0},{0,1},{1,0},{0,-1}};  ///上,右,下,左

bool chk(int x,int y)         ///判断走的点是否合法
{
    if(x > 0 && y >0 && x <= n && y <= m && maze[x][y] != '#')
        return true;
    return false;
}

int bfs(node st)
{
    int f = 0;
    vis[st.x][st.y][st.dir][st.blue] = true;
    priority_queue<node>q;
    q.push(st);
    int ans = -1;
    while(!q.empty())
    {
        node u = q.top();
        q.pop();
        if(maze[u.x][u.y] == 'T' && u.blue == 0)   ///找到T点并且颜色标记为0
        {
            return u.dis;
            continue;
        }
        for(int i = 0;i < 4;i++)
        {

            int tx = u.x+dir[i][0];
            int ty = u.y+dir[i][1];
            node newnode;                          
            ///这里行走方式只能直走,其他转向只计算增加的时间,不进行行走
            if(i == u.dir && chk(tx,ty))           ///向前走的点合法
            {
                newnode.x = tx;
                newnode.y = ty;
                newnode.dir = i;
                newnode.dis = u.dis+1;
                newnode.blue = (u.blue+1)%5;
            }
            else if(i == u.dir+2 || i == u.dir-2)  ///向后转180度
            {
                newnode = u;
                newnode.dir = i;
                newnode.dis += 2;
            }
            else                                   ///向旁边转90度
            {
                newnode = u;
                newnode.dir = i;
                newnode.dis += 1;
            }
            if(!vis[newnode.x][newnode.y][i][newnode.blue])   ///检查这种状态是否访问过
            {
                q.push(newnode);
                vis[newnode.x][newnode.y][i][newnode.blue] = true;
            }
        }
    }
    return ans;
}

int main(void)
{
    int T = 1;
    while(scanf("%d%d",&n,&m) && (n||m))
    {
        memset(vis,false,sizeof vis);
        node st;
        for(int i = 1;i <= n;i++)
        {
            scanf("%s",maze[i]+1);
            for(int j = 1;j <= m;j++)
                if(maze[i][j] == 'S')
                    st.x = i,st.y = j,st.dir = 0,st.dis = 0,st.blue = 0;
        }
        int ans = bfs(st);
        if(T != 1) puts("");      ///细节要求好高
        printf("Case #%d\n",T++);
        if(ans == -1)
            printf("destination not reachable\n");
        else
            printf("minimum time = %d sec\n",ans);
    }
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值