UVA-11624 Fire! (BFS)

这题的一个做法是先对 'F' BFS一遍,这样就可以记录下来火势到达每一个格子最早的时间,对于每个 '.' 来说,在这个火到达的最早时间之前,joe是可以走的。然后对joe BFS 一遍就可以了,重点是joe在”边上“,也就是最左边最右边最上边最下边。

特别的是,我在bfs中采用的是每产生一个新的节点就判断一下是不是终点,所以对于joe一开始就在边上的情况需要额外判断。

#include<cstdio>
#include<climits>
#include<vector>
#include<queue>
using namespace std;
struct state
{
	int x, y, th;//th表示这是第几分钟
};
const int direction[][2] = { { 0,1 },{ 0,-1 },{ 1,0 },{ -1,0 } };
int r, c, rj, cj,time[1000][1000];
char maze[1000][1002];
bool vis[1000][1000];//记录是否被访问过
vector<pair<int, int>> fire;
queue<state> q;
inline bool inlaw(int a, int b)
{
	return a >= 0 && b >= 0 && a < r&&b < c&&maze[a][b] == '.';
}
void burn()
{
	for (int i = 0; i < r; i ++ )
		for (int j = 0; j < c; j++)
			if (maze[i][j] =='.') time[i][j] = INT_MAX;
	while (!q.empty()) q.pop();
	for (int i = 0; i < fire.size(); i++)
	{
		q.push({ fire[i].first,fire[i].second,0 });
		time[fire[i].first][fire[i].second] = 0;
	}
	int nx, ny;
	while (!q.empty())
	{
		state t = q.front(); q.pop();
		for (int i = 0; i < 4; i++)
		{
			nx = t.x + direction[i][0];
			ny = t.y + direction[i][1];
			if (inlaw(nx, ny) && time[nx][ny] == INT_MAX)
			{
				time[nx][ny] = time[t.x][t.y] + 1;
				q.push({ nx,ny,0 });
			}
		}
	}
}
int bfs()
{
	while (!q.empty()) q.pop();
	q.push({ rj,cj,0 });
	while (!q.empty())
	{
		state t = q.front(); q.pop();
		for (int i = 0; i < 4; i++)
		{
			int nx = t.x + direction[i][0], ny = t.y + direction[i][1];
			if (inlaw(nx,ny)&& !vis[nx][ny]&&t.th+1<time[nx][ny])
			{
				if (!nx || !ny || nx == r - 1 || ny == c - 1) return t.th + 2;
                            //+2是因为需要从上一个位置走到边界,再走出迷宫
				q.push({ nx,ny,t.th + 1 });
				vis[nx][ny] = true;
			}
		}
	}
	return 0;
}
int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d%d", &r,&c);
		fire.clear();
		for (int i = 0; i < r; i++)
		{
			scanf("%s", maze[i]);
			for (int j = 0; j < c; j++)
			{
				if (maze[i][j] == 'J')
				{
					rj = i;
					cj = j;
					maze[i][j] = '.';
				}
				if (maze[i][j] == '.') vis[i][j] = false;
				else if (maze[i][j] == 'F') fire.push_back({ i,j });
			}
		}
		if (!rj || !cj || rj == r - 1 || cj == c - 1)
		{
			printf("1\n");
			continue;
		}
		burn();
		int t = bfs();
		if (t) printf("%d\n", t);
		else printf("IMPOSSIBLE\n");
	}
	return 0;
}

其实我本来用的是一种效率很低的土方法:

因为火可以蔓延到所有相邻的空地,但是joe不能走到火将在下一分钟蔓延到的地方,所以在对joe BFS每一层之前,先把火在这一分钟到的地方变成 ’F’。我的土土的实现方式是先记录下来所有初始的火位置,然后把下一分钟产生的火的位置记为 ‘F' ,同时记录下来新火的位置,因为只有这些火才能产生新的火。效率很低,但毕竟是我自己想的,记录一下

//这个版本使用了数组手动模拟的队列
#include<iostream>
#include<set>
using namespace std;
struct state
{
	int x, y, th;
};
const int direction[][2] = { {0,1},{0,-1},{1,0},{-1,0} };
state q[1000000];
int r, c,rj,cj,fire[50000][2],nfnum;
char maze[1000][1002];
bool vis[1000][1000];
inline bool inlaw(int a, int b)
{
	return a >= 0 && b >= 0 && a < r&&b < c&&maze[a][b]=='.';
}
void burn()
{
	set<pair<int, int>> t;
	int nx, ny;
	for(int i=0;i<nfnum;i++)
		for (int j = 0; j < 4; j++)
		{
			nx = fire[i][0]+direction[j][0], ny = fire[i][1]+direction[j][1];
			if (inlaw(nx, ny)) t.insert({ nx,ny });
		}
	nfnum = 0;
	for (set<pair<int, int>>::iterator it = t.begin(); it != t.end(); it++)
	{
		maze[(*it).first][(*it).second] = 'F';
		fire[nfnum][0] = (*it).first;
		fire[nfnum][1] = (*it).second;
		nfnum++;
	}
}
int bfs()
{
	int cur = 0, front = 0, rear = 1;
	state t;
	q[front].x = rj; q[front].y = cj;
	q[front].th = 0;
	while (front<rear)
	{
		t.x = q[front].x;
		t.y = q[front].y;
		t.th = q[front].th;
		front++;
		if (t.th != cur - 1)
		{
			cur++;
			burn();
		}
		for (int i = 0; i < 4; i++)
		{
			int nx = t.x + direction[i][0], ny = t.y + direction[i][1];
			if (inlaw(nx,ny)&& !vis[nx][ny])
			{
				if (!nx || !ny || nx == r - 1 || ny == c - 1) return t.th + 2;
				q[rear].x = nx;
				q[rear].y = ny;
				q[rear].th = t.th + 1;
				rear++;
				vis[nx][ny] = true;
			}
		}
	}
	return 0;
}
int main()
{
	int T;
	cin >> T;
	while (T--)
	{
		cin >> r >> c;
		nfnum = 0;
		for (int i = 0; i < r; i++)
		{
			cin >> maze[i];
			for (int j = 0; j < c; j++)
			{
				if (maze[i][j] == 'J')
				{
					rj = i;
					cj = j;
					maze[i][j] = '.';
				}
				if (maze[i][j] == '.') vis[i][j] = false;
				else if (maze[i][j] == 'F')
				{
					fire[nfnum][0] = i;
					fire[nfnum][1] = j;
					nfnum++;
				}
			}
		}
		if (!rj || !cj || rj == r - 1 || cj == c - 1)
		{
			cout << 1 << endl;
			continue;
		}
		int t = bfs();
		if (t) cout << t << endl;
		else cout << "IMPOSSIBLE" << endl;
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值