【NOJ1044】【算法实验三】【分支限界法】独轮车


1044.独轮车

时限:1000ms 内存限制:10000K  总时限:3000ms

描述

独轮车的轮子上有红、黄、蓝、白、绿(依顺时针序)5种颜色

在一个如下图所示的20*20的迷宫内每走一个格子,轮子上的颜色变化一次(原地转向则不变色

独轮车只能向前推或在原地转向。每走一格或原地转向90度均消耗一个单位时间。

现给定一个起点(S)和一个终点(T),求独轮车以轮子上的指定颜色(未指定车头朝向)到达终点所需的最短时间。

输入

本题包含一个测例。

测例中分别用一个大写字母表示方向和轮子的颜色,其对应关系为:E-东、S-南、W-西、N-北;R-红、Y-黄、B-蓝、W-白、G-绿。

在测试数据的第一行有以空格分隔的两个整数和两个大写字母,分别表示起点的坐标S(x,y)、轮子的颜色和开始的方向,

第二行有以空格分隔的两个整数和一个大写字母,表示终点的坐标T(x,y)和到达终点时轮子的颜色(不指定结束时方向)

从第三行开始的20行每行内包含20个字符,表示迷宫的状态。其中'X'表示建筑物,'.'表示路.

输出

在单独的一行内输出一个整数,即满足题目要求的最短时间。


#include <iostream>
#include <queue>

using namespace std;

/******************全局变量***************************/

char maze[21][21];  //存放迷宫

struct node         //记录独轮车的状态
{
    int x;      //第x行,从1开始
    int y;      //第y列,从1开始
    int color;  //车轮颜色,0==红,1==黄,2==蓝,3==白,4==旅
    int dire;   //车头朝向,0==西,1==南,2==东,3==北
};

node start,target;  //起始状态,末状态

queue <node> state; //广搜所用队列

int used[21][21][5][4];     //此状态是否到达过
int time[21][21][5][4];     //到达此状态所用的时间

/******************全局变量***************************/


/******************函数声明***************************/

void input();   //输入数据所用函数
node inputState(int x, int y, char c, char d);   //输入初始状态/末状态函数

bool bfs();     //广搜,成功返回true

bool canmoveto(node n1); //判断n1状态能否向前走一格
node moveto(node n1);    //返回n1状态向前走一格后达到的状态(同时更新used数组和time数组)

bool canturnto(node n1, int i);     //判断n1状态能否向i方向原地转向,i=0表示逆时针九十度,i=1顺时针九十度
node turnto(node n1, int i);        //返回n1状态向i方向转向后到达的状态(同时更新used数组和time数组)

bool ftarget(node n1);          //判断n1状态是不是目标状态
/******************函数声明***************************/

int main()
{
    input();    //输入数据

    if(bfs())   //广搜
    {
        //输出从初始状态到目标状态所用时间,因为目标状态只指定了坐标和颜色,
        //与“车头朝向”无关,因此设车头朝向为西,即target.dire=0
        cout<<time[target.x][target.y][target.color][0]<<endl;
    }                                                           
    else
    {
        cout<<"error"<<endl;
    }

    return 0;
}

/***********以下均为函数实现***************************/

void input()    //输入数据
{
    int x,y;
    char c,d;

    //输入初始状态
    cin>>x>>y>>c>>d;
    start=inputState(x,y,c,d);

    //输入目标状态
    cin>>x>>y>>c;
    target=inputState(x,y,c,'W');    //末状态的dire不重要,因此设为西

    //输入地图
    for(int i=1; i<21; i++)
    {
        for(int j=1; j<21; j++)
        {
            cin>>maze[i][j];
        }
    }
}

node inputState(int x, int y, char c, char d)    //输入一个状态
{
    node n1;
    n1.x=x;
    n1.y=y;
    switch(c)   //输入颜色,将字符转化为数字
    {
        case 'R':n1.color=0;break;
        case 'Y':n1.color=1;break;
        case 'B':n1.color=2;break;
        case 'W':n1.color=3;break;
        case 'G':n1.color=4;break;
    }
    switch(d)   //输入车头朝向,将字符转化为数字
    {
        case 'W':n1.dire=0;break;
        case 'S':n1.dire=1;break;
        case 'E':n1.dire=2;break;
        case 'N':n1.dire=3;break;
    }

    return n1;
}

bool bfs()      //核心算法,广搜
{
    //将初始状态入队
    state.push(start); 

    //更新used数组:标记此状态已到达
    used[start.x][start.y][start.color][start.dire]=1; 

    //更新time数组:起点到起点所用时间为0 
    time[start.x][start.y][start.color][start.dire]=0;  

    //开始搜索
    node top;
    while(!state.empty())
    {
        top=state.front();  //取队首元素
        state.pop();

        //尝试向前走一格
        if(canmoveto(top))  //如果top状态能够往前走一格
        {
            node next=moveto(top);  //获得前进一格之后的状态next
            state.push(next);   //将next状态入队
            if(ftarget(next))   //如果next状态就是target状态,退出搜索
            {
                return true;
            }
        }
        //else{cout<<"不能走"<<top.x<<' '<<top.y<<' '<<top.dire<<endl;}

        //尝试原地转向
        for(int i=0; i<2; i++)      //i=0表示逆时针转九十度,i=1表示顺时针转九十度
        {
            if(canturnto(top, i))   //如果top状态能够向i方向转九十度
            {
                node next=turnto(top, i);   //获得转向之后的状态next
                state.push(next);   //将next状态入队
                if(ftarget(next))   //如果next状态就是target状态,退出搜索
                {
                    return true;
                }
            }
        }
    }

    return false;   //如果队列为空,仍未搜到目标状态,返回false
}

//判断身处状态n1,能否向“前”走一格,“前”就是车头朝向
//不可走条件:越界、前面是墙、状态重复
bool canmoveto(node n1)
{
    switch(n1.dire)    //根据车头朝向,判断应朝哪个方向走一格
    {
        case 0: //W 西(左)
        {
            if(n1.y-1<1 //越界
                ||maze[n1.x][n1.y-1]=='X'  //或前方是墙
                ||used[n1.x][n1.y-1][(n1.color+1)%5][n1.dire]==1)    //或状态重复
            {
                return false;
            }
            break;
        }
        case 1: //N 南(下)
        {
            if(n1.x+1>20 //越界
                ||maze[n1.x+1][n1.y]=='X'  //或前方是墙
                ||used[n1.x+1][n1.y][(n1.color+1)%5][n1.dire]==1)    //或状态重复
            {
                return false;
            }
            break;
        }
        case 2: //E 东(右)
        {
            if(n1.y+1>20 //越界
                ||maze[n1.x][n1.y+1]=='X'  //或前方是墙
                ||used[n1.x][n1.y+1][(n1.color+1)%5][n1.dire]==1)    //或状态重复
            {
                return false;
            }
            break;
        }
        case 3: //B 北(上)
        {
            if(n1.x-1<1 //越界
                ||maze[n1.x-1][n1.y]=='X'  //或前方是墙
                ||used[n1.x-1][n1.y][(n1.color+1)%5][n1.dire]==1)    //或状态重复
            {
                return false;
            }
            break;
        }
    }
    return true;
}

//返回身处n1,向前走一格后达到的状态n2(并更新used数组和time数组)
node moveto(node n1)
{
    node n2;
    n2.dire=n1.dire;   //向前走一格后,车头朝向依然不变
    n2.color=(n1.color+1)%5;   //颜色向前轮转一个,防止向上溢出

    switch(n1.dire)    //根据车头朝向,判断前进一格后的横纵坐标
    {
        case 0: //西(左)
        {
            n2.x=n1.x;
            n2.y=n1.y-1;    //面向西走一格
            break;
        }
        case 1: //南(下)
        {
            n2.x=n1.x+1;
            n2.y=n1.y;
            break;
        }
        case 2: //东(右)
        {
            n2.x=n1.x;
            n2.y=n1.y+1;
            break;
        }
        case 3: //北(上)
        {
            n2.x=n1.x-1;
            n2.y=n1.y;
            break;
        }
    }

    //更新used数组,标记n2状态已到达过
    used[n2.x][n2.y][n2.color][n2.dire]=1;

    //更新time数组,到达n2状态,比到达n1状态多用了一个时间
    time[n2.x][n2.y][n2.color][n2.dire] = 1 + time[n1.x][n1.y][n1.color][n1.dire];

    return n2;
}

//判断能否向i方向原地转向,0==逆时针九十度,1==顺时针九十度
//不可转条件:状态重复
bool canturnto(node n1, int i)
{
    if(i==0)    //逆时针转九十度
    {
        if(used[n1.x][n1.y][n1.color][(n1.dire+1)%4]==1)
        {
            return false;
        }
    }
    else
    {
        if(used[n1.x][n1.y][n1.color][(n1.dire+4-1)%4]==1)
        {
            return false;
        }
    }
    return true;
}

//返回身处n1,向i方向转九十度后到达的状态n2(并更新used数组和time数组)
node turnto(node n1, int i)
{
    node n2;
    n2.x=n1.x;  //原地转向,位置不变
    n2.y=n1.y;
    n2.color=n1.color;  //原地转向,颜色不变

    if(i==0)    //逆时针转九十度
    {
        n2.dire=(n1.dire+1)%4;      //更新车头朝向,防止向上溢出
    }
    else        //顺时针转九十度
    {
        n2.dire=(n1.dire+4-1)%4;    //更新车头朝向,防止向下溢出
    }

    //更新used数组,标记n2状态已到达
    used[n2.x][n2.y][n2.color][n2.dire]=1;

    //更新time数组,从起点到达n2状态,比从起点到n1状态,多用了一个时间
    time[n2.x][n2.y][n2.color][n2.dire] = 1 + time[n1.x][n1.y][n1.color][n1.dire];

    return n2;
}

//判断n1是否到达目标状态
bool ftarget(node n1)
{
    //目标状态的dire不重要,故不加入判断条件
    if(n1.x==target.x&&n1.y==target.y&&n1.color==target.color)  //若到达目标状态
    {
        //将从起点到达状态n1(dire=任意)的时间,赋值给我们指定的目标状态target(dire=0)
        time[target.x][target.y][target.color][0]=time[n1.x][n1.y][n1.color][n1.dire];  
        return true;                                                                    
    }
    else
    {
        return false;
    }
}

【后记】

1.第一次感觉代码写的心力交瘁……累skr人……大概也不会有人看这么又臭又长的代码叭……

猜你喜欢

转载自blog.csdn.net/qq_41727666/article/details/82960414
今日推荐