宽度优先搜索题目题解
1.拯救公主
题目:
多灾多难的公主又被大魔王抓走啦!国王派遣了第一勇士阿福去拯救她。
身为超级厉害的术士,同时也是阿福的好伙伴,你决定祝他一臂之力。你为阿福提供了一张大魔王根据地的地图,上面标记了阿福和公主所在的位置,以及一些不能够踏入的禁区。你还贴心地为阿福制造了一些传送门,通过一个传送门可以瞬间转移到任意一个传送门,当然阿福也可以选择不通过传送门瞬移。传送门的位置也被标记在了地图上。此外,你还查探到公主所在的地方被设下了结界,需要集齐K种宝石才能打开。当然,你在地图上也标记出了不同宝石所在的位置。
你希望阿福能够带着公主早日凯旋。于是在阿福出发之前,你还需要为阿福计算出他最快救出公主的时间。
地图用一个R×C的字符矩阵来表示。字符S表示阿福所在的位置,字符E表示公主所在的位置,字符#表示不能踏入的禁区,字符$表示传送门,字符.表示该位置安全,数字字符0至4表示了宝石的类型。阿福每次可以从当前的位置走到他上下左右四个方向上的任意一个位置,但不能走出地图边界。阿福每走一步需要花费1个单位时间,从一个传送门到达另一个传送门不需要花费时间。当阿福走到宝石所在的位置时,就视为得到了该宝石,不需要花费额外时间。
题目条件:
1.第一行是一个正整数T\((1 <= T <= 10)\),表示一共有T组数据。
2.每一组数据的第一行包含了三个用空格分开的正整数R、C\((2 <= R, C <= 200)\)和K,表示地图是一个R×C的矩阵,而阿福需要集齐K种宝石才能够打开拘禁公主的结界。
3.接下来的R行描述了地图的具体内容,每一行包含了C个字符。字符含义如题目描述中所述。保证有且仅有一个S和E。$的数量不超过10个。宝石的类型在数字0至4范围内,即不会超过5种宝石。
分析:
跟普通的宽搜一样,用一个结构体队列,标记数组状态\(b[x][y][z]\)表示走到点\((x,y)\)时的宝石获得情况,这里每一种宝石是一个位,我们只需要用位运算就能把宝石加进去,不需要用一个7维标记数组。所以当这个位置上的字符是E的时候并且Z等于\(2^k-1\)时就输出答案结束。还有传送门也很简单,只需要在末尾考虑即可。
程序:
#include<bits/stdc++.h>
using namespace std;
char a[201][201];
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int t,m,n,s,k,b[201][201][32],c[21];
struct dd
{
int x,y,s,step;
};
queue<dd> q;
int bfs()
{
while(!q.empty())
{
dd t=q.front();
q.pop();
for(int i=0;i<4;i++)
{
int tx=t.x+dx[i];
int ty=t.y+dy[i];
if(tx<1||ty<1||tx>m||ty>n||a[tx][ty]=='#')
continue;
int ns=t.s;
if(a[tx][ty]>='0'&&a[tx][ty]<='4')
ns=ns|(1<<(a[tx][ty]-'0'));
if(b[tx][ty][ns])
continue;
if(a[tx][ty]=='E'&&ns==pow(2,k)-1)
return t.step+1;
b[tx][ty][ns]=1;
q.push(dd{tx,ty,ns,t.step+1});
if(a[tx][ty]=='$')
{
for(int j=1;j<=s;j+=2)
if(!b[c[j]][c[j+1]][ns])
{
b[c[j]][c[j+1]][ns]=1;
q.push(dd{c[j],c[j+1],ns,t.step+1});
}
}
}
}
return -1;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>t;
while(t--)
{
cin>>m>>n>>k;
memset(b,0,sizeof(b));
while(!q.empty())
q.pop();
s=0;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
cin>>a[i][j];
if(a[i][j]=='S')
{
q.push(dd{i,j,0,0});
b[i][j][0]=0;
}
else
{
if(a[i][j]=='$')
{
c[++s]=i;
c[++s]=j;
}
}
}
int ans=bfs();
if(ans==-1)
cout<<"oop!"<<'\n';
else
cout<<ans<<'\n';
}
return 0;
}
2.鸣人和佐助
题目:
佐助被大蛇丸诱骗走了,鸣人在多少时间内能追上他呢?
已知一张地图(以二维矩阵的形式表示)以及佐助和鸣人的位置。地图上的每个位置都可以走到,只不过有些位置上有大蛇丸的手下,需要先打败大蛇丸的手下才能到这些位置。鸣人有一定数量的查克拉,每一个单位的查克拉可以打败一个大蛇丸的手下。假设鸣人可以往上下左右四个方向移动,每移动一个距离需要花费1个单位时间,打败大蛇丸的手下不需要时间。如果鸣人查克拉消耗完了,则只可以走到没有大蛇丸手下的位置,不可以再移动到有大蛇丸手下的位置。佐助在此期间不移动,大蛇丸的手下也不移动。请问,鸣人要追上佐助最少需要花费多少时间?
题目条件:
1.输入的第一行包含三个整数:M,N,T。代表M行N列的地图和鸣人初始的查克拉数量T。\(0 < M,N < 200,0 ≤ T < 200\)
2.后面是M行N列的地图,其中@代表鸣人,+代表佐助。*代表通路,#代表大蛇丸的手下。
3.输出包含一个整数R,代表鸣人追上佐助最少需要花费的时间。如果鸣人无法追上佐助,则输出-1。
分析:
这道题目的状态前两个照样是位置,最后一个可以表示剩余的查克拉数量,但是如果用这个来写结构体,计算空间复杂度后发现相当于开32000000的int数组,很有可能内存超过128MB,保险起见我们还是使用队列模拟,每次我们都要考虑查克拉够不够往前走,如果对面是大蛇丸手下且身上没有查克拉就不能走过去,按这个状态写即可。
程序:
#include<bits/stdc++.h>
using namespace std;
char a[201][201];
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int m,n,z;
bool b[201][201][201];
struct dd
{
int x,y,z,step;
};
queue<dd> q;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>m>>n>>z;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
cin>>a[i][j];
if(a[i][j]=='@')
{
q.push(dd{i,j,z,0});
b[i][j][z]=1;
}
}
int l=1,r=1;
while(l<=r)
{
for(int i=0;i<4;i++)
{
int p=0;
int tx=q.front().x+dx[i];
int ty=q.front().y+dy[i];
if(a[tx][ty]=='#')
p=1;
if(tx<1||ty<1||tx>m||ty>n||q.front().z<p||b[tx][ty][q.front().z-p])
continue;
r++;
q.push(dd{tx,ty,q.front().z-p,q.front().step+1});
b[tx][ty][q.front().z-p]=1;
if(a[tx][ty]=='+')
{
cout<<q.front().step+1<<'\n';
return 0;
}
}
q.pop();
l++;
}
cout<<-1<<'\n';
return 0;
}
浙公网安备 33010602011771号