题解 P4872 【OIer们的东方梦】

· · 题解

调了一天,终于过了~写篇题解纪念一下~

感觉我的代码比起其他人的可读性要高许多。。。

感谢disangan233提供神仙题面,感谢上海爱丽丝幻乐团提供信仰,能有信心磕这道题的估计大部分是車万党吧。。。

废话不多说:我们先来分析一下题:

这题细节很多,过样例却WA掉的可以看看代码中的具体注释。

我采用了优先队列BFS的方法,于是每个点有几个属性:坐标,dis值,是否抢剑,是否偷花

我们再定义一个NB值,什么都没有时NB=0,有花时NB=1,有剑时NB=2.

分析每个字符:

0,1,2,3都好说,就是NB值不一样时距离不同。

4:偷花不需要时间,可以直接修改属性。

5:注意可以不要剑,所以要考虑要和不要两种情况。

M:关于麻薯。。。藏起来就好,别让幽幽子看到。

X:最麻烦的一个,可以满地图乱窜,每次都枚举复杂度是不行的,但是你发现每个NB值状态下最多只用传送一次就行,没必要传来传去,直接传到最后一次的位置不就行了。除非你专程去偷花或抢剑,那样会使你的NB值增加,所以每个NB值下都可以考虑传送,传送过就可以忽略X了。

那就看代码吧:具体操作都在代码里了~

自我感觉可读性良好。。。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define QWQ cout<<"QwQ"<<endl;
#define ll long long 
#include<vector>
#include<queue>
#include<stack>
#include<map>
using namespace std;
const int N=101010;
const int qwq=303030;
const int inf=0x3f3f3f3f;
int n,m;
int g,h,s,t;                         //起点和终点的坐标 
char z[1234][1234];
int dis[1234][1234][3];              //每个NB值状态下的dis值 
bool bayunzi[4];                     //在这个NB值状态下是否用过间隙 
int ff[] = {1,0,0,-1};
int gg[] = {0,1,-1,0};               //4个方向 
struct E{
    int d;
    int i,j; 
    bool lou,hua;                    //lou:是否有楼观剑,hua:是否抢过花 
};
priority_queue <E> q;                //优先队列,对d优先 
inline bool operator < (E aa,E bb) { return aa.d > bb.d; }

inline int getNB(E v) {             //获得NB值 
    if(v.lou) return 2;
    if(v.hua) return 1;
    return 0;
}

inline void check(E v) {            //更新dis值,并压入队列 
    int NB = getNB(v);
    if(v.d >= dis[v.i][v.j][NB]) return ;
    dis[v.i][v.j][NB] = v.d;     q.push(v);
}

void jian_xi(E u) {                     //从间隙往别的间隙走 
    int NB = getNB(u);
    if(bayunzi[NB]) return ; bayunzi[NB] = 1;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++) {
        if(z[i][j]!='X') continue;
        E v = (E){u.d+1, i, j, u.lou, u.hua};
        check(v);
    }
}

int BFS() {
    memset(dis,0x3f,sizeof(dis));
    q.push( (E){0,g,h,0,0} );
    while(!q.empty()) {
        E u = q.top(); q.pop();
        if(u.i==s && u.j==t) return u.d;    //终于到终点了!
        for(int k=0;k<4;k++) {              //4个方向 
            E v; 
            v.lou = u.lou; 
            v.hua = u.hua;
            v.i = u.i + ff[k];
            v.j = u.j + gg[k];             //下一个点的信息 
            int NB = getNB(v);
            if(v.i<0 || v.j<0 || v.i>n || v.j>m) continue;  //边界 
            int cl = z[v.i][v.j];           
            if(cl=='1') {
                v.d = u.d + 1;
                if(NB==2) check(v);         //楼观剑砍墙 
            }
            if(cl=='0' || cl=='X') {        //注意:‘X’是可以穿的!
                v.d = u.d + 1;
                check(v);
            }
            if(cl=='2') {
                if(NB!=0) v.d = u.d + 1;
                else      v.d = u.d + 4;    //不同NB值状态下距离不同。 
                check(v);
            }
            if(cl=='3') {
                if(NB!=0) v.d = u.d + 1;
                else      v.d = u.d + 9; 
                check(v);
            }
            if(cl=='4') {
                v.d = u.d + 1; v.hua = 1;   //偷花。
                check(v);
            }
            if(cl=='5') {                   //注意楼观剑要check两次,因为可以不选。 
                v.d = u.d + 1; 
                check(v); 
                if(!v.lou) v.lou = 1, v.d += 5;           //获得需5秒 
                check(v);
            }
        }
        if(z[u.i][u.j]=='X') jian_xi(u);                  //从间隙飞 
    }
    return -1;
}

int main() {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%s",z[i]+1);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++) {
        if(z[i][j]=='M') z[i][j] = '0';                  //把麻薯变成0藏起来,别让幽幽子吃了。 
        if(z[i][j]=='S') g = i, h = j, z[i][j] = '0';    //注意起点是可以走好几次的,它的属性改成0。
        if(z[i][j]=='E') s = i, t = j, z[i][j] = '0';    
    }
    int ans = BFS();
    if(ans!=-1) printf("%d",ans);
    else printf("We want to live in the TouHou World forever");    //此生无悔入东方,来世炸了幻想乡。 
    return 0;
}

看起来长,其实很瘦的,压下行就短了:)