P6742 [BalticOI 2014 Day2] Portals

P6742 [BalticOI 2014 Day2] Portals

Description

给定一个 \(R\times C\) 的迷宫,每个格子都有一种方块:

  • # 墙,不可以走,不可以穿过
  • . 路,可以走
  • S 出生点,玩家从这里开始走,只有一个
  • C 终点,玩家要到达这里,只有一个

现在要对迷宫进行扩容,周围要增加一个方块 #,变成 \((R+2)\times (C+2)\) 的迷宫。

并且在任何时候,它都可以向上、左、下、右四个方向中的一个发射传送门。当一个传送门被发射,它会一直向发射的方向飞行,直到碰触到墙壁。这时,传送门会被放置在这堵墙上。

走一格需要 \(1\) 的时间,穿梭传送门也需要 \(1\) 的时间。

求从出生点到终点最少需要多少时间。

Solution

我们简单造几组数据后发现有一种最优策略为两个传送门在同一点释放。于是我们考虑建边策略,我们首先可以把点 \(u\) 上下左右相邻的并且在图内的点连边。

而后我们考虑传送门的连边方法,我们先预处理出向各个方向可以到的墙的距离,而后我们比较这四种方案,得出一种走到传送门前等待传送的最小 \(dis\) 并把这个点连接这四个墙点,边权均为 \(dis+1\)。注意若 \(u\) 与某墙点长度恰好是 \(dis\) 也就意味着直接走不需要传送就可以花费 \(dis\) 到达,因而这条最小边(可能不唯一)边权应为 \(dis\) 而非 \(dis+1\)。这样我们就完成了建边,直接在图上跑最短路即可。

Code

#include<bits/stdc++.h>
using namespace std;

const int N=1e3+7;
int n,r,c;
char mp[N][N];
int s,t;
int lf[N][N],rg[N][N],up[N][N],dn[N][N];
vector<pair<int,int>> G[N*N];
int dis[N*N];
bool _vis[N*N];

struct node{
	int dis,id;
	friend bool operator < (node a,node b){
		return a.dis>b.dis;
	}
};

void Dj(int s){
	memset(_vis,0,sizeof _vis);
	priority_queue<node> q;
	memset(dis,80,sizeof dis);
	dis[s]=0;
	q.push({0,s});
	while(!q.empty()){
		int u=q.top().id;
		q.pop();
		if(_vis[u]) continue;
		_vis[u]=1;
		for(auto i:G[u]){
			int k=i.first,w=i.second;
			if(dis[k]>dis[u]+w){
				dis[k]=dis[u]+w;
				q.push({dis[k],k});
			}
		}
	}
}

bool query(int x,int y){
	return (mp[x][y]=='.'||mp[x][y]=='S'||mp[x][y]=='C');
}

int get(int x,int y){
	return x*c+y-c;
}

void add(int u,int v,int w){
	G[u].push_back(make_pair(v,w));
}

void read(){
	scanf("%d%d",&r,&c);
	n=r*c;
	for(int i=1;i<=r;i++){
		for(int j=1;j<=c;j++){
			cin>>mp[i][j];
			if(mp[i][j]=='S') s=get(i,j);
			if(mp[i][j]=='C') t=get(i,j);
		}
	}
}

void deal(){
	for(int i=1;i<=r;i++){
		for(int j=1;j<=c;j++){
			if(mp[i][j]=='#') lf[i][j]=j;
			else lf[i][j]=lf[i][j-1];
		}
	}
	for(int j=1;j<=c;j++){
		for(int i=1;i<=r;i++){
			if(mp[i][j]=='#') up[i][j]=i;
			else up[i][j]=up[i-1][j];
		}
	}
	for(int i=1;i<=r;i++){
		rg[i][c+1]=c+1;
		for(int j=c;j;j--){
			if(mp[i][j]=='#') rg[i][j]=j;
			else rg[i][j]=rg[i][j+1];
		}
	}
	for(int j=1;j<=c;j++){
		dn[r+1][j]=r+1;
		for(int i=r;i;i--){
			if(mp[i][j]=='#') dn[i][j]=i;
			else dn[i][j]=dn[i+1][j];
		}
	}
}

void work(){
	for(int i=1;i<=r;i++){
		for(int j=1;j<=c;j++){
			if(query(i,j)){
				int L=lf[i][j]+1,R=rg[i][j]-1,U=up[i][j]+1,D=dn[i][j]-1;
				int _dis=min({j-L+1,R-j+1,i-U+1,D-i+1});
				int _L=get(i,L),_R=get(i,R),_U=get(U,j),_D=get(D,j);
				int _pos=get(i,j);
				add(_pos,_L,min(_dis,j-L)),add(_pos,_R,min(_dis,R-j)),add(_pos,_U,min(_dis,i-U)),add(_pos,_D,min(_dis,D-i));
				if(query(i,j+1)) add(_pos,get(i,j+1),1); 
				if(query(i+1,j)) add(_pos,get(i+1,j),1); 
				if(query(i-1,j)) add(_pos,get(i-1,j),1); 
				if(query(i,j-1)) add(_pos,get(i,j-1),1);
			}
		}
	}
	Dj(s);
	printf("%d\n",dis[t]);
//	for(int i=1;i<=n;i++) printf("%d ",dis[i]);
}

int main(){
	read();
	deal();
	work();
	return 0;
}
posted @ 2023-07-25 14:22  Zimo_666  阅读(24)  评论(1编辑  收藏  举报