-
第一个一遍过的题目,基本没有看题解
-
记忆搜索当中,注意初始化成一个特殊值;而在访问之后,每个点就会有一个不同于INF(或者 -1等特殊标记)的值
先到先得,适者生存(初始值可以被任何人替代)
-
这道题目当中剪枝是必要的,因为有许多无效状态,根本不用入队,相当于进行了剪枝操作
-
首先,需要对状态进行一个严格的约定:
比如这里,约定每一个节点将要出去时候的状态为需要加入队列的状态
-
举例来说:两次带有同样的氧气罐从同一个位置进入P点,更晚到达是否可能出现更好的结果(相同的氧气状态可不可以根据到来的早晚剪枝)
这里做了一步证明
加速丸如果需要使用,必须要在到达终点之前使用才能得到效果,且和位置无关
假设终点为 T
P 到 T至少有1步路,所以加速丸必然可以使用
所以可以在第一步使用
假设P1 = P2 ,更晚到达者去到其他任何点,并不会产生时间上的优势(因为和位置无关),所以保留时间最短者即可
特殊标志点,需要格外注意是否需要增加标志位!
-
对于每一个氧气罐节点,如果没地方装了,那这个节点就是无效了,剪枝呗
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;
}