OpenJudge-7221:拯救公主

7221:拯救公主


Description
多灾多难的公主又被大魔王抓走啦!国王派遣了第一勇士阿福去拯救她。
身为超级厉害的术士,同时也是阿福的好伙伴,你决定祝他一臂之力。你为阿福提供了一张大魔王根据地的地图,上面标记了阿福和公主所在的位置,以及一些不能够踏入的禁区。你还贴心地为阿福制造了一些传送门,通过一个传送门可以瞬间转移到任意一个传送门,当然阿福也可以选择不通过传送门瞬移。传送门的位置也被标记在了地图上。此外,你还查探到公主所在的地方被设下了结界,需要集齐K种宝石才能打开。当然,你在地图上也标记出了不同宝石所在的位置。
你希望阿福能够带着公主早日凯旋。于是在阿福出发之前,你还需要为阿福计算出他最快救出公主的时间。
地图用一个R×C的字符矩阵来表示。字符S表示阿福所在的位置,字符E表示公主所在的位置,字符#表示不能踏入的禁区,字符$表示传送门,字符.表示该位置安全,数字字符0至4表示了宝石的类型。阿福每次可以从当前的位置走到他上下左右四个方向上的任意一个位置,但不能走出地图边界。阿福每走一步需要花费1个单位时间,从一个传送门到达另一个传送门不需要花费时间。当阿福走到宝石所在的位置时,就视为得到了该宝石,不需要花费额外时间。

Input
第一行是一个正整数T(1 <= T <= 10),表示一共有T组数据。
每一组数据的第一行包含了三个用空格分开的正整数R、C(2 <= R, C <= 200)和K,表示地图是一个R×C的矩阵,而阿福需要集齐K种宝石才能够打开拘禁公主的结界。
接下来的R行描述了地图的具体内容,每一行包含了C个字符。字符含义如题目描述中所述。保证有且仅有一个S和E。$的数量不超过10个。宝石的类型在数字0至4范围内,即不会超过5种宝石。
Output
对于每一组数据,输出阿福救出公主所花费的最少单位时间。若阿福无法救出公主,则输出“oop!”(只输出引号里面的内容,不输出引号)。每组数据的输出结果占一行。

Sample Input

1
7 8 2
........
..S..#0.
.##..1..
.0#.....
...1#...
...##E..
...1....

Sample Output

11

题目解析
这道题就是一道迷宫的升级版…所以我们继续使用迷宫的广度优先搜索模板——求得最短路径(时间)。
选好模板后就要对模板进行修改了。

  1. 由于有多组数据,我们可以将要使用的变量全部定义在循环内部,每一次循环结束后都会被释放,下一次重新定义就避免了清0变量的操作。
  2. 对输入进行修改——在迷宫中加入了传送门。我们需要定义一个二维int数组(door_coord)——第一维表示传送门的个数,第二维表示传送门的坐标和一个int变量(sdoor_coord)表示传送门的个数。然后在输入的for循环中,在判断起点位置的下方同时判断是否是传送门,若是,则存入door_coord。
  3. 我们还需要一些变量,如判断是否重复走到同一个位置的数组(walked),我们可以用一个三维bool数组表示——第一、二维表示坐标,第三维表示宝石种类,注意第三维——不能直接使用宝石的数量,因为可能它获得了不同的宝石但是宝石的总数量相同!因此我们可以用二进制表示获得的宝石种类(例如:获得宝石0,3 –> 2进制:01001[2]=9[10])。
  4. 结构体需要增加一些变量——bool变量(enchantment)表示结界是否存在(true表示有结界);bool数组(get_jewel)表示获得的宝石
  5. 对广度优先搜索进行改变。在判断是否超界或是否为墙完毕后,判断该位置是不是宝石(即字符是不是‘0’到‘4’之间的字符)。如果是,则将get_jewel的值改变。遍历 get _jewel,将获得宝石的二进制表示(参照3)和获得宝石的总数量统计出来,然后判断是否走了重复的路(用walked)。若不是,则判断得到的宝石数量是否达标,若是,则将enchantment改为false(结界消失)。如果是传送门,则遍历door_coord,将除它本身的传送门存入队列。如果找到终点,则判断enchantment是否为false,若是,则输出,作找到路径的标记并且结束数据;若enchantment为true,注意不要退出本次循环,将终点当做道路进行下一次移动
  6. 若没有找到则输出“oop!”。

程序样例

#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
struct POINT
{
    int x,y,tot; //坐标,步数
    bool get_jewel[7],enchantment; //找到的宝石,结界
} point,door;
int main()
{
    int k,F[4][2]={-1,0,1,0,0,-1,0,1};
    scanf("%d",&k);
    for(int kk=1;kk<=k;kk++)
    {
        /*定义*/
        queue<POINT> que; //队列
        int r,c,needkind,door_coord[15][2]={},sdoor_coord=0; //边界,需要的宝石种类,传送门坐标,传送门个数
        char maze[205][205]={};//迷宫
        bool walked[205][205][35]={},finish=false; //已经走过的路,第三维是宝石数(2进制);是否完成
        /*输入*/
        scanf("%d%d%d",&r,&c,&needkind);
        for(int i=0;i<r;i++)
        {
            scanf("%s",maze[i]);
            for(int j=0;j<c;j++)
            {
                if(maze[i][j]=='S') //起点
                {
                    memset(point.get_jewel,false,sizeof(point.get_jewel));
                    point.x=i;point.y=j;point.tot=0;point.enchantment=true;
                }
                if(maze[i][j]=='$') //传送门
                {
                    door_coord[sdoor_coord][0]=i; //存入位置
                    door_coord[sdoor_coord++][1]=j;
                }
            }
        }
        /*广度优先搜索*/
        que.push(point); //存入起点
        while(!que.empty()) //队列不为空
        {
            for(int i=0;i<4;i++) //4个方向
            {
                point=que.front(); //直接等
                point.tot++;point.x+=F[i][0];point.y+=F[i][1]; //改坐标,改步数
                if(point.x<0 || point.x>=r || point.y<0 || point.y>=c || maze[point.x][point.y]=='#') continue;
                //超界或为墙
                if('0'<=maze[point.x][point.y] && maze[point.x][point.y]<='4') //如果为宝石
                    point.get_jewel[maze[point.x][point.y]-'0']=true; //改变point中该宝石的值为 “找到 ”
                int sget_jewel=0,ss=0; //找到的宝石个数
                for(int i=0;i<5;i++)
                    if(point.get_jewel[i]) //遍历宝石获得数
                        sget_jewel+=pow(2,i),ss++; //eg:获得0,3 --> 2进制:01001=9
                if(walked[point.x][point.y][sget_jewel]) continue;
                //如果之前到达过该位置且未找到更多的宝石
                walked[point.x][point.y][sget_jewel]=true; //改变该位置的到达记录
                if(ss>=needkind) point.enchantment=false; //如果找到了足够的宝石,结界消失
                if(maze[point.x][point.y]=='$') //如果是传送门
                {
                    door=point; //保存位置
                    for(int i=0;i<sdoor_coord;i++) //遍历所有传送门
                        if(door_coord[i][0]!=point.x || door_coord[i][1]!=point.y) //不是当前的位置
                        {
                            door.x=door_coord[i][0];door.y=door_coord[i][1]; //存入传送门
                            que.push(door);
                        }
                }
                if(maze[point.x][point.y]=='E' && !point.enchantment)
                //如果是找到终点并且没有结界
                {
                    printf("%d\n",point.tot);
                    finish=true; //找到
                }
                if(finish) break; //找到退出
                que.push(point); //存入当前位置
            }
            if(finish) break; //找到退出
            que.pop(); //出队
        }
        if(!finish) printf("oop!\n"); //没有找到
    }
    return 0;
}
  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这道题目的要求是输入一个电话号码,然后判断这个电话号码是否合法。一个电话号码必须满足以下条件: 1. 共有11位数字; 2. 第一位数字必须是1; 3. 第2-3位数字必须是3、4、5、7、8中的一个; 4. 第4-7位数字必须是0-9中的数字; 5. 第8-11位数字必须是0-9中的数字。 如果一个电话号码符合以上的5个条件,则认为这个电话号码是合法的,否则就是不合法的。 以下是这道题目的C++代码实现: ```c++ #include <iostream> #include <string> using namespace std; bool is_valid_phone_number(string phone_number) { if (phone_number.size() != 11 || phone_number[0] != '1') { return false; } char second_char = phone_number[1]; char third_char = phone_number[2]; if (second_char != '3' && second_char != '4' && second_char != '5' && second_char != '7' && second_char != '8') { return false; } for (int i = 3; i < 11; i++) { if (phone_number[i] < '0' || phone_number[i] > '9') { return false; } } return true; } int main() { string phone_number; cin >> phone_number; if (is_valid_phone_number(phone_number)) { cout << "YES" << endl; } else { cout << "NO" << endl; } return 0; } ``` 首先,我们定义了一个函数`is_valid_phone_number`,这个函数接受一个字符串`phone_number`作为参数,然后判断这个电话号码是否合法。在函数中,我们先判断电话号码的长度和第一位是否为1,如果不符合这两个条件,则直接返回false。接着,我们判断第二位和第三位是否为3、4、5、7、8中的一个,如果不是,则返回false。最后,我们遍历电话号码的第4-11位,判断是否都是数字,如果不是,则返回false。如果电话号码符合以上的5个条件,则返回true。 在`main`函数中,我们首先读入一个电话号码,然后调用`is_valid_phone_number`函数来判断这个电话号码是否合法。如果合法,则输出"YES",否则输出"NO"。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值