BDFZOI 拯救公主

难度:提高-

题目类型:BFS

提交次数:3

涉及知识:BFS

描述

多灾多难的公主又被大魔王抓走啦!国王派遣了第一勇士阿福去拯救她。

身为超级厉害的术士,同时也是阿福的好伙伴,你决定祝他一臂之力。你为阿福提供了一张大魔王根据地的地图,上面标记了阿福和公主所在的位置,以及一些不能够踏入的禁区。你还贴心地为阿福制造了一些传送门,通过一个传送门可以瞬间转移到任意一个传送门,当然阿福也可以选择不通过传送门瞬移。传送门的位置也被标记在了地图上。此外,你还查探到公主所在的地方被设下了结界,需要集齐K种宝石才能打开。当然,你在地图上也标记出了不同宝石所在的位置。

你希望阿福能够带着公主早日凯旋。于是在阿福出发之前,你还需要为阿福计算出他最快救出公主的时间。

地图用一个R×C的字符矩阵来表示。字符S表示阿福所在的位置,字符E表示公主所在的位置,字符#表示不能踏入的禁区,字符$表示传送门,字符.表示该位置安全,数字字符0至4表示了宝石的类型。阿福每次可以从当前的位置走到他上下左右四个方向上的任意一个位置,但不能走出地图边界。阿福每走一步需要花费1个单位时间,从一个传送门到达另一个传送门不需要花费时间。当阿福走到宝石所在的位置时,就视为得到了该宝石,不需要花费额外时间。

输入第一行是一个正整数T(1 <= T <= 10),表示一共有T组数据。

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

样例输入

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

样例输出

11

代码:
  1 #include<iostream>
  2 #include<cstring>
  3 #include<queue>
  4 #include<vector>
  5 #include<cmath>
  6 using namespace std;
  7 int t;
  8 char map[205][205];
  9 bool diamond[5];
 10 struct node{
 11     int x, y;
 12     int jewel,time;
 13     node(int xx, int yy, int j, int t):x(xx),y(yy),jewel(j),time(t){}
 14 };
 15 bool operator<(const node&a, const node&b){
 16     return a.time>b.time;
 17 }
 18 priority_queue<node> pq;
 19 bool visited[205][205][20];
 20 int dirx[4] = {0,0,1,-1};
 21 int diry[4] = {-1,1,0,0};
 22 vector<node>v;
 23
 24 bool judge(int x, int y){
 25     if(map[x][y]!=‘#‘&&map[x][y]!=0)
 26         return true;
 27     return false;
 28 }
 29
 30 bool isjewel(int x, int y){
 31     if(map[x][y]==‘0‘||map[x][y]==‘1‘||map[x][y]==‘2‘||map[x][y]==‘3‘||map[x][y]==‘4‘)
 32         return true;
 33     return false;
 34 }
 35
 36 bool finished(int n){
 37     int temp = 0;
 38     for(int i = 1; i <= 32; i*=2){
 39         temp++;
 40         if((i&n)==0&&diamond[temp-1]!=0)
 41             return false;
 42     }
 43     return true;
 44 }
 45 int main(){
 46     cin>>t;
 47     while(t--){
 48         int r, c, k;
 49         cin>>r>>c>>k;
 50         memset(map, 0, sizeof(map));
 51         memset(visited,0,sizeof(visited));
 52         memset(diamond,0,sizeof(diamond));
 53         while(!pq.empty())pq.pop();
 54         v.clear();
 55         int i, j;
 56         for(i = 1; i <= r; i++)
 57             for(j = 1; j <= c; j++){
 58                 cin>>map[i][j];
 59                 if(map[i][j]==‘S‘){
 60                     pq.push(node(i,j,0,0));
 61                     visited[i][j][0] = true;
 62                 }
 63                 else if(map[i][j]==‘$‘)
 64                     v.push_back(node(i,j,0,0));
 65                 else if(map[i][j]>=‘0‘&&map[i][j]<=‘4‘){
 66                     diamond[map[i][j]-‘0‘] = true;
 67                 }
 68             }
 69             int flag = 0;
 70         while(!pq.empty()){
 71             node no = pq.top();
 72             pq.pop();
 73         //    cout<<no.x<<" "<<no.y<<" "<<no.jewel<<" "<<no.time<<":"<<endl;
 74             if(map[no.x][no.y]==‘E‘&&finished(no.jewel)){
 75                 cout<<no.time<<endl;
 76                 flag = 1;
 77                 break;
 78             }
 79             for(i = 0; i < 4; i++){
 80                 int nx = no.x+dirx[i];
 81                 int ny = no.y+diry[i];
 82                 int nj = no.jewel;
 83                 if(isjewel(nx,ny)){
 84                     if((int(pow(2,map[nx][ny]-‘0‘))&no.jewel)==0){
 85                         nj+=pow(2, map[nx][ny]-‘0‘);
 86                     }
 87                 }//位运算 加钻石
 88                 if(!visited[nx][ny][nj]&&judge(nx,ny)){
 89                     if(map[nx][ny]==‘$‘){
 90                         for(j = 0; j < v.size(); j++){
 91                             pq.push(node(v[j].x,v[j].y,nj,no.time+1));
 92                             visited[v[j].x][v[j].y][nj] = true;
 93         //                    cout<<v[j].x<<" "<<v[j].y<<" "<<nj<<" "<<no.time+1<<endl;
 94
 95                         }
 96                     }
 97                     else{
 98                         pq.push(node(nx,ny,nj,no.time+1));
 99                         visited[nx][ny][nj] = true;
100         //                cout<<nx<<" "<<ny<<" "<<nj<<" "<<no.time+1<<endl;
101                     }
102                 }
103             }
104 //            getchar();
105         }
106         if(!flag) cout<<"oop!"<<endl;
107     }
108     return 0;
109 }

备注:

昨天调到一点。。然后就睡觉了。今天起来给调过了。

掌握了调试BFS的好方法。。如代码中注释所示。

这种带各种附加条件的BFS就是在裸的BFS外面包了一层纸,只要把结点的状态设置丰富一点就行了。用老师的话说图是“立体”的,虽然我觉得这个比喻反而不容易理解。

对于传送门的处理方式是,如果下一步走到的是传送门,就同时把所有的传送门(输入时存在了vector里)加到队列里。

最近老犯的错误就是一个语句块不加大括号。。

加钻石那块也弄错来着。我用的是一个数存(因为是5种钻石所以不超过32-1)。老师给数据之前不知道钻石序号不挨着。

判断finished用了2的5次方的复杂度,其实可以在外面算出来finished的jewel大小,这样就把判断的复杂度降成O(1)了。一会儿我再改一改。

很重要的一个问题,就是因为涉及到位运算,优先级搞错了好几次!

优先级 操作符 描述 例子 结合性
1 ()
[]
->
.
::
++
--
调节优先级的括号操作符
数组下标访问操作符
通过指向对象的指针访问成员的操作符
通过对象本身访问成员的操作符
作用域操作符
后置自增操作符
后置自减操作符
(a + b) / 4;
array[4] = 2;
ptr->age = 34;
obj.age = 34;
Class::age = 2;
for( i = 0; i < 10; i++ ) ...
for( i = 10; i > 0; i-- ) ...
从左到右
2 !
~
++
--
-
+
*
&
(type)
sizeof
逻辑取反操作符
按位取反(按位取补) 
前置自增操作符
前置自减操作符
一元取负操作符
一元取正操作符
解引用操作符
取地址操作符
类型转换操作符
返回对象占用的字节数操作符
if( !done ) ...
flags = ~flags;
for( i = 0; i < 10; ++i ) ...
for( i = 10; i > 0; --i ) ...
int i = -1;
int i = +1;
data = *ptr;
address = &obj;
int i = (int) floatNum;
int size = sizeof(floatNum);
从右到左
3 ->*
.*
在指针上通过指向成员的指针访问成员的操作符
在对象上通过指向成员的指针访问成员的操作符
ptr->*var = 24;
obj.*var = 24;
从左到右
4 *
/
%
乘法操作符
除法操作符
取余数操作符
int i = 2 * 4;
float f = 10 / 3;
int rem = 4 % 3;
从左到右
5 +
-
加法操作符
减法操作符
int i = 2 + 3;
int i = 5 - 1;
从左到右
6 <<
>>
按位左移操作符
按位右移操作符
int flags = 33 << 1;
int flags = 33 >> 1;
从左到右
7 <
<=
>
>=
小于比较操作符
小于或等于比较操作符
大于比较操作符
大于或等于比较操作符
if( i < 42 ) ...
if( i <= 42 ) ...
if( i > 42 ) ...
if( i >= 42 ) ...
从左到右
8 ==
!=
等于比较操作符
不等于比较操作符
if( i == 42 ) ...
if( i != 42 ) ...
从左到右
9 & 按位与操作符 flags = flags & 42; 从左到右
10 ^ 按位异或操作符 flags = flags ^ 42; 从左到右
11 | 按位或操作符 flags = flags | 42; 从左到右
12 && 逻辑与操作符 if( conditionA && conditionB ) ... 从左到右
13 || 逻辑或操作符 if( conditionA || conditionB ) ... 从左到右
14 ? : 三元条件操作符 int i = (a > b) ? a : b; 从右到左
15 =
+=
-=
*=
/=
%=
&=
^=
|=
<<=
>>=
赋值操作符
复合赋值操作符(加法)
复合赋值操作符(减法)
复合赋值操作符(乘法)
复合赋值操作符(除法)
复合赋值操作符(取余)
复合赋值操作符(按位与)
复合赋值操作符(按位异或)
复合赋值操作符(按位或)
复合赋值操作符(按位左移)
复合赋值操作符(按位右移)
int a = b;
a += 3;
b -= 4;
a *= 5;
a /= 2;
a %= 3;
flags &= new_flags;
flags ^= new_flags;
flags |= new_flags;
flags <<= 2;
flags >>= 2;
从右到左
16 , 逗号操作符 for( i = 0, j = 0; i < 10; i++, j++ ) ... 从左到右

从http://www.cnblogs.com/ywl925/p/3710246.html抄过来的。注意按为与(&)比==低!!!其他的优先级也要注意一下。

时间: 2024-10-06 01:19:35

BDFZOI 拯救公主的相关文章

BDFZOI 树的直径

提交次数:2 涉及知识:基础图论/BFS 描述 一棵树T的"直径"定义为结点两两间距离的最大值.给定带权树T,求T的直径长度. 输入 第一行包含2个整数N.M,表示图中共有N个结点和M条无向边.(N <= 5000,M<n)接下来M行,每行包含3个整数{u,v,w},表示有一条无向边连接结点u.v*输入保证是无环图输出一个整数,代表直径长度 样例输入 4 31 2 12 3 22 4 3 样例输出 5 代码: 1 #include<iostream> 2 #in

BDFZOI 迷宫问题

难度:普及 题目类型:BFS 提交次数:2 涉及知识:BFS 描述 定义一个二维数组: int maze[5][5] = { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, }; 它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线. 输入一个5 × 5的二维数组,表示一个迷宫.数据保证有唯一解.输出左上角到右下角的最短路径

poj 4105 拯救公主(bfs)

原题网址:http://bailian.openjudge.cn/practice/4105/ 思路: 每个位置包括的状态:所在的位置,获得的宝石. 广搜:用队列存储达到某个位置时,获得的宝石完全相同的最少用时. 传送门另外考虑即可. 详细代码: 1 #include <cstdio> 2 #include <queue> 3 #include <cstring> 4 using namespace std; 5 6 const int INF = 0x44444444

noi 7221 拯救公主 (状态压缩+bfs)

/* 这题实在调糊了 借鉴的题解的一些判断方法 位运算大法好 - - 因为要集齐所有的宝石所以状态压缩一下 f[i][j][s]将s化为二进制 每一0表示该宝石没有 1表示该宝石有 有:到(i,j)这个点时 宝石收集状况为s的状态是否存在 */ #include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; int T,n,m,k,f[210

BDFZOI bdfz历险记

难度:提高- 题目类型:BFS 提交次数:2 涉及知识:BFS 描述 公主又又又被大魔王抓走啦!这次公主被困在一个叫做"bdfz"的迷宫里,作为勇者的你必须穿越这个迷宫才能找到公主.在迷宫中除了有道路和墙壁,你还会遇到各种各样的事件,影响你的血量,并且同一事件可以反复触发,例如: '1':起太晚上学迟到,血量-1 '2':考太差惨遭嘲讽,血量-2 '5':上课睡着没听课,血量-5 'X':导师约谈,血量+1 'Y':幡然悔悟,血量+5 假设进入迷宫时你的起始血量为L,每移动1个位置需要

bzoj3007: 拯救小云公主

Description 英雄又即将踏上拯救公主的道路…… 这次的拯救目标是——爱和正义的小云公主. 英雄来到boss的洞穴门口,他一下子就懵了,因为面前不只是一只boss,而是上千只boss.当英雄意识到自己还是等级1的时候,他明白这就是一个不可能完成的任务. 但他不死心,他在想,能不能避开boss去拯救公主呢,嘻嘻. Boss的洞穴可以看成一个矩形,英雄在左下角(1,1),公主在右上角(row,line).英雄为了避开boss,当然是离boss距离越远越好了,所以英雄决定找一条路径使到距离bo

【BZOJ】【3007】拯救小云公主

思路题 我的naive的做法是二分答案+判定是否有路径可走……但是没有正确理解[走的方向任意]这句话…… 其实就是说想咋走咋走= =360°无死角乱走…… 所以其实是个平面上的问题…… 我们可以换个方向来考虑……二分一个答案,判断英雄走到/走不到公主那里,是不是就等价于,boss控制的区域连起来了使得英雄走不到公主那里了?(狼抓兔子的即视感) 所以我们可以转化成从上边&左边,在boss之间走,使得走到下边&右边 路径上最大的一条边(边权代表着如果英雄从这两个boss之间经过,离两个boss

hdu2102

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2102 题目: A计划 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 8731    Accepted Submission(s): 2076 Problem Description 可怜的公主在一次次被魔王掳走一次次被骑士们救回来之后,

卿学姐与魔法(优先队列)

个人心得:思路很简单,不过就是会超时,而且直接用数组的话肯定不够大. 所以就用优先队列,让里面只装N个数就好了,然后再次添加时进行比较,比他小就放进去. 不过这样超时,所以先将A,B排序,然后只要比队首大就break就可以过了. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<queue> 6 #include<