Stealing Harry Potter's Precious

传送门
给出一个起点,4个终点,要求从起点出发,每个终点都走到的最短距离

一般来说,如果只有一个终点,那么就直接bfs,但是有4个终点,用状压dp

设置一个vis[N][N][1 << 4]来设置记录

且每一个点存4个变量,x,y,key和step,key表示当前的状压情况

那么不断进行bfs,如果遇到终点,那么进行状压一下u.key | (1 << (s[xx][yy] - '0'))即可,同时更新key即可

那么如果说nex.key == (1 << k) - 1,也就是说4个终点都走过了,那么就直接返回答案即可

第一次遇到状压bfs,记录下,感觉好神奇啊,大雾

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <cmath>
#include <stack>
#include <algorithm>
#include <ctime>
using namespace std;
#define ll long long
const int N = 105;
char s[N][N];
int dir[][2] = {1, 0, -1, 0, 0, 1, 0, -1};
int n, m, k;
int sx, sy;
bool vis[N][N][1 << 4]; // 状压数组
struct Node{
    int x, y, key, step;
};
bool check(int x, int y){
    if(x < 1 || x > n || y < 1 || y > m || s[x][y] == '#') return 0;
    return 1;
}
int bfs(){
    queue<Node> q;
    Node now;
    now.x = sx, now.y = sy;
    now.step = 0; now.key = 0;
    q.push(now);
    while(!q.empty()) {
        Node u = q.front();
        q.pop();
        for(int i = 0; i < 4; i++) {
            int xx = dir[i][0] + u.x;
            int yy = dir[i][1] + u.y;
            if(!check(xx, yy)) continue;
            Node nex;
            if(s[xx][yy] >= '0' && s[xx][yy] <= '3') { //终点,状压一下,再继承上一个点的信息
                if(!vis[xx][yy][u.key]) {
                    vis[xx][yy][u.key | (1 << (s[xx][yy] - '0'))] = 1;
                    nex.x = xx, nex.y = yy, nex.step = u.step + 1, nex.key = u.key | (1 << (s[xx][yy] - '0'));
                    if(nex.key == (1 << k) - 1) return nex.step; // 点走完了
                    q.push(nex);
                }
            }else { // 普通点,继承上一个点的信息
                if(!vis[xx][yy][u.key]) {
                    vis[xx][yy][u.key] =  1;
                    nex.x = xx, nex.y = yy, nex.key = u.key, nex.step = u.step + 1;
                    q.push(nex);
                }
            }
        }
    }
    return -1;
}
int main(){
    while(~scanf("%d%d", &n, &m)) {
        if(n == 0 && m == 0) break;
        bool f = 1;
        for(int i = 1; i <= n; i++) scanf("%s", s[i] + 1);
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= m; j++){
                if(s[i][j] == '@') sx = i, sy = j; // 起点
            }
        }
        memset(vis, 0, sizeof(vis));
        scanf("%d", &k);
        for(int i = 0; i < k; i++) {
            int x, y;
            scanf("%d%d", &x, &y);
            if(s[x][y] == '#') f = 0;
            else if(s[x][y] == '@') {  // 把所有的终点设置为相应的数字,方便进行状压
                s[x][y] = i + '0';
                vis[x][y][1 << i] = 1;
            } else s[x][y] = i + '0';
        }
        if(!f) printf("-1
");
        else {
            printf("%d
", bfs());
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Emcikem/p/13675425.html