HDU 3681 Prison Break (二分答案+狀壓DP+bfs預處理)

題意:

機器人從F出發,走到G可以充滿電,走到Y關掉開關,D不能走進,要求把所有開關關掉,且機器人存儲的電量最少,並求出該最小電量。


分析:

把F,G,Y三種類型的點找出來,進行討論,首先bfs處理出來各個點之間的最短路。現在題目就變成了:求一幅圖中滿足走遍所有Y點的條件,機器人存儲的電量最少。

由於點最多隻有15個,狀壓dp來處理。二分機器人的儲電量,每次用dp來做判定條件。


#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int n,m;
char map[22][22];
int dist[22][22],vis[22][22];
int dx[] = {0,0,-1,1};
int dy[] = {1,-1,0,0};
int dp[22][1<<16];
struct node {
    int x,y,step;
    char c;
}q[1111],p[20];
int head,tail,cnt,st,numY;

int bfs(int x,int y,int xx,int yy) {
    head = 0; tail = 0;
    memset(vis,0,sizeof(vis));
    q[head].x = x;
    q[head].y = y;
    q[head++].step = 0;
    vis[x][y] = 1;
    while(head != tail) {
        node t = q[tail++];
        node tt;
        if(t.x == xx && t.y == yy) {
            return t.step;
        }
        for(int i=0; i<4; i++) {
            tt.x = t.x + dx[i];
            tt.y = t.y + dy[i];
            if(tt.x<0 || tt.x>=n || tt.y<0 || tt.y >=m || map[tt.x][tt.y] == 'D' || vis[tt.x][tt.y] ) continue;
            tt.step = t.step + 1;
            vis[tt.x][tt.y] = 1;
            q[head++] = tt;
        }
    }
    return -1;
}

void getdist() {
    for(int i=0; i<cnt; i++) {
        for(int j=i; j<cnt; j++) {
            if(i == j) dist[i][j] = 0;
            else {
                dist[i][j] = bfs(p[i].x,p[i].y,p[j].x,p[j].y);
                dist[j][i] = dist[i][j];
            }
        }
    }
}

int getY(int x) {
    int id = 0;
    int num = 0;
    while(x) {
        if((x & 1) && p[id].c == 'Y') num ++;
        x = x >> 1;
        id ++;
    }
    return num;
}

int judge(int va) {
    memset(dp,-1,sizeof(dp));
    int total = 1 << cnt;
    dp[st][1 << st] = va;
    for(int j=0; j<total; j++) {
        if((j & (1 << st)) == 0) continue;
        for(int i=0; i<cnt; i++) {
            if(i == st) continue;
            for(int k=0; k<cnt; k++) {
                if(dist[i][k] == -1) continue;
                if(dist[i][k] <= dp[k][j ^ (1 << i)]) {
                    if(p[i].c == 'G') dp[i][j] = va;
                    else dp[i][j] = max(dp[i][j],dp[k][j ^ (1 << i)] - dist[i][k]);
                }
            }
        }
    }
    for(int j=0; j<total; j++) {
        if((j & (1 << st)) == 0) continue;
        if(getY(j) == numY) {
            for(int i=0; i<cnt; i++) {
                if(p[i].c == 'Y' && dp[i][j] != -1) return 1;
            }
        }
    }
    return 0;
}

void solve() {
    getdist();
    int l = 1, r = 250, mid;
    int minn = 251;
    while(l <= r) {
        mid = (l + r) >> 1;
        if(judge(mid)) {
            minn = min(minn,mid);
            r = mid - 1;
        } else l = mid + 1;
    }
    if(minn == 251) puts("-1");
    else printf("%d\n",minn);
}

int main() {
    while(scanf("%d%d",&n,&m)) {
        if(n == 0 && m == 0) break;
        cnt = 0; numY = 0;
        for(int i=0; i<n; i++) scanf("%s",map[i]);
        for(int i=0; i<n; i++) {
            for(int j=0; j<m; j++) {
                if(map[i][j] == 'F') {
                    p[cnt].x = i; p[cnt].y = j;
                    p[cnt].c = 'F';
                    st = cnt;
                    cnt ++;
                }
                if(map[i][j] == 'G') {
                    p[cnt].x = i; p[cnt].y = j;
                    p[cnt].c = 'G';
                    cnt++;
                }
                if(map[i][j] == 'Y') {
                    numY ++;
                    p[cnt].x = i; p[cnt].y = j;
                    p[cnt].c = 'Y';
                    cnt++;
                }
            }
        }
        solve();
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章