ACM/ICPC 2018亚洲区预选赛北京赛站网络赛 【bfs + 记忆化搜索 + 剪枝】 AC 代码

ACM 北京区域赛 bfs+剪枝+ms

  1. 第一个一遍过的题目,基本没有看题解

  2. 记忆搜索当中,注意初始化成一个特殊值;而在访问之后,每个点就会有一个不同于INF(或者 -1等特殊标记)的值

    先到先得,适者生存(初始值可以被任何人替代)

  3. 这道题目当中剪枝是必要的,因为有许多无效状态,根本不用入队,相当于进行了剪枝操作

    1. 首先,需要对状态进行一个严格的约定:

      比如这里,约定每一个节点将要出去时候的状态为需要加入队列的状态

    2. 举例来说:两次带有同样的氧气罐从同一个位置进入P点,更晚到达是否可能出现更好的结果(相同的氧气状态可不可以根据到来的早晚剪枝)

      这里做了一步证明

      加速丸如果需要使用,必须在到达终点之前使用才能得到效果,且和位置无关

      假设终点为 T

      P 到 T至少有1步路,所以加速丸必然可以使用

      所以可以在第一步使用

      假设P1 = P2 ,更晚到达者去到其他任何点,并不会产生时间上的优势(因为和位置无关),所以保留时间最短者即可

      特殊标志点,需要格外注意是否需要增加标志位

    3. 对于每一个氧气罐节点,如果没地方装了,那这个节点就是无效了,剪枝呗

      NAIVE!

      你没地方装了,并不代表你不能走这个地方

      仍然需要做一次比较

       if(c == 'B'){ 
                           // 氧气瓶
                           if(o!=5 && nxt.t <= dp[nxti][nxtj][o+1]){
                               // 氧气没有拿满
                               // 还可以再拿一个
                               nxt.o++;
                           }else{
                               continue; // 剪枝 
                           }
                       }else if(c == '#'){
                           if(o == 0) continue;
                           else{
                               nxt.o--; nxt.t++;
                           } 
                       }else if(c == 'P'){
                           // 药丸
                           nxt.t--; // 因为已经加一 
                       }  
                       
                       // 约定,检测在结束之后的状态
                       // 经过证明,这一步并不受 P的影响 
                       if(dp[nxti][nxtj][nxt.o] <= nxt.t) continue;  

 

// https://hihocoder.com/problemset/problem/1828
// ms 记忆化搜索 剪枝
#include<stdio.h>
#include<queue>
#include<algorithm>
using namespace std;
#define N 105
#define INF 0x7fffffff 

struct node{
	int i,j,o,t;
	node(){
		i = j = o = t = 0;
	};
	node(int _i,int _j,int _o,int _t):i(_i),j(_j),o(_o),t(_t){
	};
	
};

char map[N][N];
int dp[N][N][6];

//const int ddd = 1;
const int ddd = 0;
const int di[4] = {-1,1,0,0}; 
const int dj[4] = {0,0,-1,1};
int ans; 

int n;
int m;
int ki,kj,ti,tj;

bool check(int i,int j){
	return (i<n&&i>=0)&&(j<m&&j>=0);
} 

void bfs(){
	// cur nxt 命名法
	queue<node> q; 
	ans = INF;
	
	for(int i = 0;i<n;i++){
		for(int j = 0;j<m;j++){
			for(int k = 0;k<6;k++){
				// 和 visit复用 
				dp[i][j][k] = INF;
			}
		}
	}
	
	q.push(node(ki,kj,0,0));
	
	dp[0][0][0] = 0;
	
	while(!q.empty()){
		node cur = q.front(); q.pop();
		int ii = cur.i,jj = cur.j, o = cur.o, t = cur.t;
		if(ii == ti && jj == tj){
			ans = min(ans,t);
		}
		if(ddd){
			printf(" 队列大小[%d] 队首 %c[%d][%d] O【%d】 T【%d】\n",q.size(),map[ii][jj],ii,jj,o,t);
		}
		
		for(int i = 0;i<4;i++){
			node nxt = cur;
			int nxti = ii + di[i];
			int nxtj = jj + dj[i];
			nxt.i = nxti; nxt.j = nxtj;
			nxt.o = o;
			nxt.t = t+1;
			if(check(nxti,nxtj)){
				// 可以访问
//				if(ddd) printf("准备访问%c[%d][%d]\n",map[nxti][nxtj],nxti,nxtj); 
				char c = map[nxti][nxtj];
				if(c == 'B'){ 
					// 氧气瓶
					if(o!=5 && nxt.t <= dp[nxti][nxtj][o+1]){
						// 氧气没有拿满
						// 还可以再拿一个
						nxt.o++;
					}else{
						continue; // 剪枝 
					}
				}else if(c == '#'){
					if(o == 0) continue;
					else{
						nxt.o--; nxt.t++;
					} 
				}else if(c == 'P'){
					// 药丸
					nxt.t--; // 因为已经加一 
				}  
				
				// 约定,检测在结束之后的状态
				// 经过证明,这一步并不受 P的影响 
				if(dp[nxti][nxtj][nxt.o] <= nxt.t) continue; 

				dp[nxti][nxtj][nxt.o] = nxt.t;
				q.push(nxt);
			}
		} 
	}
	
}

int main(){
	
	if(ddd) freopen("E:/in.txt","r",stdin); 
	
	char b[1000];
	while(true){
		gets(b);
		sscanf(b,"%d %d",&n,&m);
		if(n==0 && m==0){
			break;
		}
		
		for(int i = 0;i<n;i++){
			gets(map[i]);
			for(int j = 0;j<m;j++){
				// 悟空和唐僧的位置 
				if(map[i][j] == 'S') ki = i, kj = j;
				if(map[i][j] == 'T') ti = i, tj = j; 
			}
		}
		
		bfs();
		
		if(ans == INF) printf("-1\n");
		else printf("%d\n",ans);
	} 
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值