• HDU 3681 Prison Break(状压DP + BFS)题解


    题意:一张图,F是起点,Y是必须要到的点,D不能走,G可以充电。可以往四个方向走,每走一步花费一个电,走到G可以选择充满电或者不充,每个G只能充一次。问你走遍Y的最小初始点亮。number(G) + number(Y) <= 15

    思路:显然Y和G都只要用一次就行,那么状压YG状态。然后BFS出任意YG两点最短路,状压DP。用&判断最终结果是不是当前状态的子集。

    代码:

    #include<set>
    #include<map>
    #include<cmath>
    #include<queue>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include <iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const int maxn = 15;
    const int M = maxn * 30;
    const ull seed = 131;
    const int INF = 0x3f3f3f3f;
    const int MOD = 1e4 + 7;
    int dp[1 << maxn][maxn];
    char mp[maxn + 10][maxn + 10];
    int n, m, sx, sy;
    int cnt;
    int id[maxn * maxn + 20];
    int bettery[maxn * maxn + 20];
    int getid(int x, int y){
        return id[x * m + y];
    }
    struct Node{
        int x, y, step;
    };
    int dis[maxn * maxn + 10][maxn * maxn + 10];
    int vis[maxn + 5][maxn + 5];
    int to[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};
    void bfs(int x, int y){
        memset(vis, 0, sizeof(vis));
        Node a, b;
        queue<Node> q;
        while(!q.empty()) q.pop();
        vis[x][y] = 1;
        a.x = x, a.y = y, a.step = 0;
        q.push(a);
        while(!q.empty()){
            a = q.front();
            q.pop();
            if(mp[a.x][a.y] != 'D' && mp[a.x][a.y] != 'S') dis[getid(x, y)][getid(a.x, a.y)] = a.step;
            for(int i = 0; i < 4; i++){
                b.x = a.x + to[i][0];
                b.y = a.y + to[i][1];
                if(b.x < 0 || b.y < 0 || b.x >= n || b.y >= m) continue;
                if(vis[b.x][b.y]) continue;
                if(mp[b.x][b.y] == 'D') continue;
                vis[b.x][b.y] = 1;
                b.step = a.step + 1;
                q.push(b);
            }
        }
    }
    
    int Fin;
    bool check(int st){
        memset(dp, -1, sizeof(dp));
        for(int i = 0; i < cnt; i++){
            if(dis[cnt][i] <= st){
                if(bettery[i]) dp[1 << i][i] = st;
                else dp[1 << i][i] = st - dis[cnt][i];
            }
        }
    
        for(int i = 0; i < (1 << cnt); i++){
            for(int j = 0; j < cnt; j++){
                if(dp[i][j] == -1) continue;
                if(!((1 << j) & i)) continue;
                if((i & Fin) == Fin) return true;
                for(int k = 0; k < cnt; k++){
                    if((1 << k) & i) continue;
                    if(dis[j][k] > dp[i][j]) continue;
                    dp[i | (1 << k)][k] = max(dp[i][j] - dis[j][k], dp[i | (1 << k)][k]);
                    if(bettery[k]) dp[i | (1 << k)][k] = st;
                }
            }
        }
    
        return false;
    }
    
    int main(){
        while(~scanf("%d%d", &n, &m) && n + m){
            cnt = 0;
            Fin = 0;
            int tmp = 0;
            memset(bettery, 0, sizeof(bettery));
            for(int i = 0; i < n; i++){
                scanf("%s", mp[i]);
                for(int j = 0; j < m; j++){
                    if(mp[i][j] == 'F'){
                        sx = i, sy = j;
                    }
                    else if(mp[i][j] == 'G'){
                        bettery[cnt] = 1;
                        id[i * m + j] = cnt++;
                    }
                    else if(mp[i][j] == 'Y'){
                        Fin += (1 << cnt);
                        id[i * m + j] = cnt++;
                        tmp++;
                    }
                }
            }
            id[sx * m + sy] = cnt;
    
            if(tmp == 0){
                printf("0
    ");
                continue;
            }
    
            memset(dis, INF, sizeof(dis));
            for(int i = 0; i < n; i++){
                for(int j = 0; j < m; j++){
                    if(mp[i][j] != 'D' && mp[i][j] != 'S')
                        bfs(i, j);
                }
            }
    
            int l = 0, r = 1000000;
            int ans = -1;
            while(l <= r){
                int m = (l + r) >> 1;
                if(check(m)){
                    r = m - 1;
                    ans = m;
                }
                else l = m + 1;
            }
            printf("%d
    ", ans);
        }
        return 0;
    }
  • 相关阅读:
    内置方法(item系列、__str__方法、__del__方法)
    POJ3436
    CF551B
    HDU1588
    HDU3117
    CF834D
    CF832D
    CF832C
    POJ1930
    POJ3666
  • 原文地址:https://www.cnblogs.com/KirinSB/p/11019304.html
Copyright © 2020-2023  润新知