1. 程式人生 > 實用技巧 >Stealing Harry Potter's Precious - 狀壓bfs入門

Stealing Harry Potter's Precious - 狀壓bfs入門

傳送門
給出一個起點,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\n");
        else {
            printf("%d\n", bfs());
        }
    }
    return 0;
}