补题:2018ACM-ICPC北京网络赛 题A-Saving Tang Monk II

题目链接:
Saving Tang Monk II

题目大意:
给你一个n*m的图,你要从S走到T,每走一步都要花费1的时间,现在有的房间会有不同,’.'代表空房间,走入后不会发生什么,'B’代表有氧气瓶的房间,每走入一次就能得到一罐氧气瓶(但持续呆在里面不会得到,氧气瓶的携带上限是5个),‘P’代表有加速药丸的房间,每走入一次都代表可以减少1的时间(携带无上限,其实可以直接认为是走进这个房间不消耗时间,题目就是为了迷惑人写这么多),’#'代表有毒气的房间,每走入一次需要一罐氧气瓶,并且在其中还有呆一的时间(即走入要消耗1个氧气瓶,消耗2的时间),问这从S走到T的最少花费时间,走不到输入-1。

解题思路:
Emmm,比赛中用了BFS+三维vis去写,结果是过不了最后一个样例,因为发现第一次访问到T的不一定是最少时间访问。gg
于是,试试最短路+分层图
将图分成6层,即0-5个氧气瓶作为分的标准
然后遇到#就往下走一层,时间+2,遇到P就往上走一层,时间+0,其他就是在当前层图走,时间+1。
代码实现即开3维的dis[x][y][ox],第三维代表氧气数,同样vis[x][y][ox]也是开三维
然后用dijstra跑一遍最短路即可求出答案

AC代码:

#include <bits/stdc++.h>

using namespace std;
const int INF = 0x3f3f3f3f;
char g[150][150];

int dis[150][150][6],vis[150][150][6];

int n,m;
int dir[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};

struct node{
	int x,y;
	int ox,dis;
	node(){}
	node(int _x, int _y, int _ox, int d):x(_x),y(_y),ox(_ox),dis(d){}
	bool operator<(const node &a)const{
		return dis==a.dis? ox > a.ox : dis > a.dis;
	}
};

void dijstra(int x,int y){
	priority_queue<node> pq;
	pq.push(node(x,y,0,0));
	dis[x][y][0] = 0;
	while(!pq.empty()){
		node tmp = pq.top();
		pq.pop();
		if(vis[tmp.x][tmp.y][tmp.ox]) continue;
		vis[tmp.x][tmp.y][tmp.ox] = 1;
		for(int i=0; i<4; ++i){
			int tx = tmp.x + dir[i][0];
			int ty = tmp.y + dir[i][1];
			if(tx < 0 || tx >=n || ty <0 || ty >=m) continue;
			if(g[tx][ty]== '#'){
				if(tmp.ox<=0) continue;
				if(dis[tx][ty][tmp.ox-1] > tmp.dis+2){
					dis[tx][ty][tmp.ox-1] = tmp.dis+2;
					pq.push(node(tx,ty,tmp.ox-1,tmp.dis+2));
				}
			}
			else if(g[tx][ty] == 'B'){
				if(dis[tx][ty][tmp.ox+1] > tmp.dis+1){
					dis[tx][ty][tmp.ox+1] = tmp.dis+1;
					pq.push(node(tx,ty,tmp.ox+1,tmp.dis+1));
				}
			}
			else if(g[tx][ty] == 'P'){
				if(dis[tx][ty][tmp.ox] > tmp.dis){
					dis[tx][ty][tmp.ox] = tmp.dis;
					pq.push(node(tx,ty,tmp.ox,tmp.dis));
				}
			}else{ //S, T或 . 
				if(dis[tx][ty][tmp.ox] > tmp.dis+1){
					dis[tx][ty][tmp.ox] = tmp.dis+1;
					pq.push(node(tx,ty,tmp.ox,tmp.dis+1));
				}
			}
		}
	}
}

int main(){
	
	while(~scanf("%d%d",&n,&m)&&n){
		int sx,sy,ex,ey;
		for(int i=0; i<n; ++i){
			scanf("%s",g[i]);
			for(int j=0; j<m; ++j){
				if(g[i][j] == 'S') {
					sx = i; sy =j;
				}
				if(g[i][j] == 'T'){
					ex = i; ey = j;
				}
			}	
		}
		
		memset(vis,0,sizeof(vis));
		memset(dis,INF,sizeof(dis));
		int ans = INF;
		dijstra(sx,sy);
		for(int i=0; i<=5; ++i){
			ans = min(ans, dis[ex][ey][i]);
		}
		
		if(ans==INF) cout<<"-1"<<endl;
		else cout<<ans<<endl;
	}
	
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值