COCI 2016/2017 Round #3,November 25th,2017 Portal [bfs+Dijkstra]

题意:给你n*m图,这个图中有墙,起点,终点,一个人从起点开始往终点走,他有一把枪,可以对四个方向射击,射到的墙会产生传送门,地图上只能存在两个传送门,可以相互连通,求从C到F的最小步数。

题解:对于一个点来说,首先肯定是可以向四个方向走1步,然后假如用传送门的话,肯定是往一个方向射击,然后走到最近的墙射击然后传送,这样的话,我们需要预处理出所有点四个方向的墙的位置,与距离这个点最近的墙的位置,然后最短路跑出C到F 的距离。

AC代码:

#include<stdio.h>
#include<queue>
#include<stdlib.h>
#include<string.h>
#define INF 1000000005
using namespace std;
char a[505][505];
int n,m;
struct node
{
	int x,y;
	node(){}
	node(int x,int y)
	{
		this->x=x;
		this->y=y;
	}
};
struct point
{
	int x,y,w;
	point(){}
	point(int x,int y,int w)
	{
		this->x=x;
		this->y=y;
		this->w=w;
	}
};
int dist[505][505],mark[505][505];
int len[505][505],fly[505][505][4][2];
int dir[4][2]={
	{1,0},
	{-1,0},
	{0,1},
	{0,-1}
};
priority_queue<point>QUE;
queue<node>que;
bool operator<(point a,point b)
{
	return a.w>b.w;
}
void bfs()
{
	memset(mark,0,sizeof(mark));
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(a[i][j]=='#')
				que.push(node(i,j)),mark[i][j]=1;
	while(!que.empty())
	{
		node k=que.front();
		que.pop();
		for(int i=0;i<4;i++)
		{
			int dx=k.x+dir[i][0];
			int dy=k.y+dir[i][1];
			if(dx<=0||dy<=0||dx>n||dy>m||mark[dx][dy]||a[dx][dy]=='#')continue;
			mark[dx][dy]=1;
			len[dx][dy]=len[k.x][k.y]+1;
			que.push(node(dx,dy));
		}
	}
}
void dij(node s)
{
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			dist[i][j]=INF,mark[i][j]=0;
	dist[s.x][s.y]=0;
	QUE.push(point(s.x,s.y,0));
	while(!QUE.empty())
	{
		point k=QUE.top();
		QUE.pop();
		if(mark[k.x][k.y])continue;
		mark[k.x][k.y]=1;
		for(int i=0;i<4;i++)
		{
			int dx=k.x+dir[i][0];
			int dy=k.y+dir[i][1];
			if(dx<=0||dy<=0||dx>n||dy>m||mark[dx][dy]||a[dx][dy]=='#')continue;
			if(dist[dx][dy]>k.w+1)
			{
				dist[dx][dy]=k.w+1;
				QUE.push(point(dx,dy,dist[dx][dy]));
			}
		}
		for(int i=0;i<4;i++)
		{
			int dx=k.x+fly[k.x][k.y][i][0];
			int dy=k.y+fly[k.x][k.y][i][1];
			if(dist[dx][dy]>k.w+len[k.x][k.y])
			{
				dist[dx][dy]=k.w+len[k.x][k.y];
				QUE.push(point(dx,dy,dist[dx][dy]));
			}
		}
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%s",a[i]+1);
	node s,e;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			if(a[i][j]=='C')s=node(i,j); 
			if(a[i][j]=='F')e=node(i,j);
		}
	bfs();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			fly[i][j][0][0]=fly[i-1][j][0][0]-1;
			fly[i][j][3][1]=fly[i][j-1][3][1]-1;
			if(a[i-1][j]=='#')fly[i][j][0][0]=0;
			if(a[i][j-1]=='#')fly[i][j][3][1]=0;
		}
	for(int i=n;i>=1;i--)
		for(int j=m;j>=1;j--)
		{
			fly[i][j][1][1]=fly[i][j+1][1][1]+1;
			fly[i][j][2][0]=fly[i+1][j][2][0]+1;
			if(a[i+1][j]=='#')fly[i][j][2][0]=0;
			if(a[i][j+1]=='#')fly[i][j][1][1]=0;
		}
	dij(s);
	if(dist[e.x][e.y]==INF)printf("nemoguce\n");
	else printf("%d\n",dist[e.x][e.y]);
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值