https://ac.nowcoder.com/acm/contest/318/H

C++版本一
题解:直接bfs,对每个状态记录一下他收集了那些快乐水。记录状态有一个小技巧,二进制压缩记录状态。(自己百度状压去)用3位二进制数记录你获得了那些快乐水。栗子:000表示你有0瓶快乐水,001表示你有第一瓶快乐水,110表示你有第二,三瓶快乐水,依次类推。一共有8个状态0-7,用vis[state][x][y]表示你从起点到达(x,y)位置且状态为sta的最小消耗。注意,当你经过快乐水的节点你可以选择捡起或不捡起来。
Ps:细节见代码,当然自己补题更好。

#include<bits/stdc++.h>
#define mme(a,b) memset((a),(b),sizeof((a)))
#define ***(x) cout<<"* "<<x<<"\n"
using namespace std;
typedef long long LL;
const int N = 1e2 + 7;
const int INF = 0x3f3f3f3f;
struct lp {
    int x, y, num, step;
} AA, BB;
int n, m, ans;
char ar[N][N];
int dir[4][2] = {1,0,0,1,-1,0,0,-1};
int vis[N][N][8];
int sx, sy, ex, ey;
int kx[4], ky[4], cost[4];
inline int get_num(int x, int y) {
    for(int i = 1; i <= 3; ++i) if(x == kx[i] && y == ky[i]) return i;
    return -1;
}
inline int get_cost(int num) {
    int ans = 0;
    for(int i = 0; i < 3; ++i) if(num & (1 << i))ans = max(ans, cost[i + 1]);
    return ans;
}
void bfs() {
    for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) for(int k = 0; k <= 7; ++k)
        vis[i][j][k] = INF;
    queue<lp> Q;
    AA.x = sx, AA.y = sy, AA.num = 0;
    AA.step = 0;
    Q.push(AA);
    vis[sx][sy][0] = 0;
    while(!Q.empty()) {
        AA = Q.front(); Q.pop();
        if(AA.x == ex && AA.y == ey && AA.num == 7) {
            ans = min(AA.step, ans);
            continue;
        }
        int cc = get_cost(AA.num);
        //printf("x = %d y = %d step = %d num = %d\n\n", AA.x, AA.y, AA.step, AA.num);
        for(int i = 0; i < 4; ++i) {
            int px = AA.x + dir[i][0];
            int py = AA.y + dir[i][1];
            if(px <= 0 || py <= 0 || px > n || py > m) continue;
            if(ar[px][py] == '#' && AA.num == 7) continue;
            BB.step = AA.step + cc + 1;
            if(BB.step >= vis[px][py][AA.num]) continue;
            BB.x = px, BB.y = py;
            BB.num = AA.num;
            vis[px][py][BB.num] = BB.step;
            Q.push(BB);
            int key_num = get_num(px, py);
            if(key_num != -1 && ((BB.num&(1<<(key_num-1))) == 0)) {
                BB.num |= (1 << (key_num-1));
                if(BB.step >= vis[px][py][BB.num]) continue;
                vis[px][py][BB.num] = BB.step;
                Q.push(BB);
            }
        }
    }
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("E://A//shuju//5.in", "r", stdin);
    freopen("E://A//shuju//5.out", "w", stdout);  
#endif
    int tim;
    scanf("%d", &tim);
    while(tim --) {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; ++i) {
            scanf("%s", ar[i] + 1);
            for(int j = 1; j <= m; ++j) {
                if(ar[i][j] == 'S') sx = i, sy = j;
                if(ar[i][j] == 'E') ex = i, ey = j;
                if(ar[i][j] == 'X') kx[1] = i, ky[1] = j;
                if(ar[i][j] == 'Y') kx[2] = i, ky[2] = j;
                if(ar[i][j] == 'Z') kx[3] = i, ky[3] = j;
            }
        }
        for(int i = 1; i <= 3; ++i) scanf("%d", &cost[i]);
        ans = INF;
        bfs();
        printf("%d\n", ans);
    }
    return 0;
}


C++版本二
题解:注意到题目说的当你没有集齐所有快乐水的每次移动花费为1.所以你可以预处理出S,X,Y,Z四点之间的曼哈顿距离。然后终点以满的花费跑一次有障碍物的普通bfs,记录到X,Y,Z的最小体力花费。然后枚举一遍X,Y,Z的收集顺序,选取最优解即可。此方法效率更高。

#include <bits/stdc++.h>
#define lson l,mid,rt<<1
#define rson mid+1,r,(rt<<1)+1
#define CLR(x,y) memset((x),y,sizeof(x))
#define ***(x) cerr << #x << "=" << x << endl
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int  T, n, m, sx, sy, lx, ly, ax, bx, cx, ay, by, cy, a, b, c;
char mp[105][105];
int dis[105][105];
bool vis[105][105];
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
int p[5] = {1, 2, 3};
struct Node {
    int x, y, val;
} e[5];
struct node {
    int x, y, stp;
    node() {}
    node(int x, int y, int stp): x(x), y(y), stp(stp) {}
};
bool ok(int x, int y) {
    if (x < 1 || y < 1 || x > n || y > m) return 0;
    else return 1;
}
void bfs(int cost) {
    CLR(vis, 0);
    CLR(dis, 0x3f);
    dis[lx][ly] = 0;
    vis[lx][ly] = 1;
    queue<node> que;
    que.push(node(lx, ly, 0));
    while (!que.empty()) {
        node tmp = que.front(); que.pop();
        dis[tmp.x][tmp.y] = tmp.stp;
        for (int i = 0; i < 4; i++) {
            int dx = tmp.x + dir[i][0];
            int dy = tmp.y + dir[i][1];
            if (!ok(dx, dy)) continue;
            if (mp[dx][dy] == '#' || vis[dx][dy]) continue;
            vis[dx][dy] = 1;
            que.push(node(dx, dy, tmp.stp + cost));
        }
    }
}
int calc(int x1, int y1, int x2, int y2) {
    return abs(x1 - x2) + abs(y1 - y2);
}
int main() {
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++) scanf("%s", mp[i] + 1);
        int tmp = 0;
        for (int i = 1, x; i <= 3; i++)  scanf("%d", &e[i].val), tmp = max(tmp, e[i].val);
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                if (mp[i][j] == 'S') {
                    sx = i;
                    sy = j;
                }else if (mp[i][j] == 'E') {
                    lx = i;
                    ly = j;
                }else if (mp[i][j] == 'X') {
                    e[1].x = i;
                    e[1].y = j;
                }else if (mp[i][j] == 'Y') {
                    e[2].x = i;
                    e[2].y = j;
                }else if (mp[i][j] == 'Z') {
                    e[3].x = i;
                    e[3].y = j;
                }
            }
        }
        bfs(tmp+1);
        int min_ans = INF;
        for(int i = 1; i <= 3; ++i) p[i] = i;
        do {
            /*for(int i = 1; i <= 3; ++i) printf("%d ", p[i]);
            printf("\n");*/
            int sum = dis[e[p[3]].x][e[p[3]].y];
            sum += calc(sx, sy, e[p[1]].x, e[p[1]].y);
            tmp = 0;
            for(int i = 1; i <= 2; ++i) {
                tmp = max(tmp, e[p[i]].val);
                sum += (tmp+1)*calc(e[p[i]].x, e[p[i]].y, e[p[i + 1]].x, e[p[i + 1]].y);
            }
            min_ans = min(min_ans, sum);
        } while (next_permutation(p + 1, p + 4));
        printf("%d\n", min_ans);
    }
    return 0;
}

C++版本三 

#include "bits/stdc++.h"
using namespace std;
const int dir[4][2] = {-1,0,0,-1,0,1,1,0};
int N, M, sx, sy, ans, a[5];
bool che(int x, int y)
{
    return x>=1 && y>=1 && x<=N && y<=M;
}
char mp[105][105];
bool vis[105][105][10];
struct node
{
    int x ,y, state, speed, t;
    node(int a=0, int b=0, int c=0, int d=0, int tt=0):x(a), y(b), state(c), speed(d), t(tt) {}
    friend bool operator < (node e1, node e2) { return e1.t > e2.t; }
};
void bfs()
{
    memset(vis, false, sizeof(vis));
    priority_queue<node> Q;
    Q.push(node(sx,sy,0,0,0));
    vis[sx][sy][0] = true;
    while(!Q.empty())
    {
        node tmp = Q.top(); Q.pop();
        for(int i=0; i<4; i++)
        {
            int xx = tmp.x + dir[i][0], yy = tmp.y + dir[i][1];
            if(!che(xx, yy) || (tmp.state == 7 && mp[xx][yy] == '#')) continue;
            if(mp[xx][yy] == 'E')
            {
                if(tmp.state == 7) { ans = tmp.t + tmp.speed + 1; return; }
                if(vis[xx][yy][tmp.state]) continue;
                vis[xx][yy][tmp.state] = true;
                Q.push(node(xx, yy, tmp.state, tmp.speed, tmp.t + tmp.speed + 1));
            }
            else if(mp[xx][yy]>='X' && mp[xx][yy]<='Z')
            {
                int id = (mp[xx][yy] - 'X');
                if( (tmp.state>>id) & 1)
                {
                    if(vis[xx][yy][tmp.state]) continue;
                    vis[xx][yy][tmp.state] = true;
                    Q.push(node(xx, yy, tmp.state, tmp.speed, tmp.t + tmp.speed + 1));
                }
                else
                {
                    if(!vis[xx][yy][tmp.state])
                    {
                        vis[xx][yy][tmp.state] = true;
                        Q.push(node(xx, yy, tmp.state, tmp.speed, tmp.t + tmp.speed + 1));
                    }
                    int state = (tmp.state | (1<<id)), speed = max(tmp.speed, a[id]);
                    vis[xx][yy][state] = true;
                    Q.push(node(xx, yy, state, speed, tmp.t + tmp.speed + 1));
                }
            }
            else
            {
                if(vis[xx][yy][tmp.state]) continue;
                vis[xx][yy][tmp.state] = true;
                Q.push(node(xx, yy, tmp.state, tmp.speed, tmp.t + tmp.speed + 1));
            }
        }
    }
}
int main()
{
    int T;  scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &N, &M);
        for(int i=1; i<=N; i++)
        {
            scanf("%s", mp[i]+1);
            for(int j=1; j<=M; j++)
            {
                if(mp[i][j] == 'S')
                {
                    sx = i;
                    sy = j;
                    mp[i][j] = '.';
                }
            }
        }
        scanf("%d%d%d", &a[0], &a[1], &a[2]);
        //ans = INF;
        bfs();
        printf("%d\n", ans);
    }
    return 0;
}