HDU 3681 Prison Break - 状压dp【TSP】

题目描述

题目大意:

一个机器人想越狱,监狱是一个N*M的网格。每走一格,耗费1个单位的电量。他只能带一定电量的电池, ‘F’表示起点, ‘S’表示道路可行, ‘D’表示不能经过的地点。 ‘G’表示充电器,只可充电一次,但是可以经过很多次。‘Y’表示要破坏的机关,也是只能破坏一次,但是可以经过无数次。
求机器人破坏掉所有机关,需要带的最小初始电量。

分析:

破坏所有的机关Y,且G,Y,F的总和不超过15,联想到状压dp中的TSP问题。
预处理出G,Y,F之间的两两最短距离。
原问题<=> 在这个新建的无向图上,从F出发,其余点只经过一次,经过每个点需要的最小出事电量。
我们发现如果初始电量不确定,dp不好做。
二分初始电量:用dp判定。dp[S][i]表示此状态下能剩的最多的电量,只要有能达到 【经过所有Y的状态 】的状态,就当前电量就合法。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#include<queue>
#define MAXN 15
#define MAXST 32768
#define INF 2000000000
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};

int n,m,dist[MAXN+10][MAXN+10],cntu,tar,id[MAXN+10][MAXN+10],w[MAXN+10][MAXN+10],mx,dp[MAXST+10][MAXN+10];
char mat[MAXN+10][MAXN+10];
bool vis[MAXN+10][MAXN+10],tag;

void bfs(int p,int q)
{
    memset(vis,0,sizeof vis);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            dist[i][j]=INF;
    int u,v,x,y;
    queue<pair<int ,int > > que;
    que.push(make_pair(p,q));
    vis[p][q]=true;
    dist[p][q]=0;
    while(!que.empty()){
        u=que.front().first,v=que.front().second;
        que.pop();
        for(int i=0;i<4;i++){
            x=u+dir[i][0],y=v+dir[i][1];
            if(x<1||x>n||y<1||y>m||vis[x][y]||mat[x][y]=='D')
                continue;
            vis[x][y]=true;
            dist[x][y]=dist[u][v]+1;
            que.push(make_pair(x,y));
        }
    }
}
void read()
{
    int p,q;
    cntu=tar=0;
    tag=false;
    memset(id,0,sizeof id);
    for(int i=1;i<=n;i++){
        scanf("%s",mat[i]+1);
        for(int j=1;j<=m;j++){
            if(mat[i][j]=='F')
                p=i,q=j;
            if(mat[i][j]=='G'||mat[i][j]=='Y'){
                id[i][j]=++cntu;
                if(mat[i][j]=='Y')
                    tar|=(1<<(id[i][j]-1));
            }
        }
    }
    bfs(p,q);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            if(id[i][j]){
                w[0][id[i][j]]=dist[i][j];
                if(dist[i][j]!=INF)
                    mx+=dist[i][j]*2;
                if(dist[i][j]==INF&&mat[i][j]=='Y'){
                    tag=true;
                    return ;
                }
            }
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            if(id[i][j]){
                bfs(i,j);
                for(p=1;p<=n;p++)
                    for(q=1;q<=m;q++)
                        if(id[p][q])
                            w[id[i][j]][id[p][q]]=dist[p][q];
            }
        }
}
bool DP(int lmt)
{
    memset(dp,-1,sizeof dp);
    for(int i=1,k;i<=cntu;i++){
        k=1<<(i-1);
        dp[k][i]=max(dp[k][i],lmt-w[0][i]);
        if(dp[k][i]>=0&&!(tar&k))
            dp[k][i]=lmt;
    }
    int S=(1<<cntu)-1;
    for(int s=0;s<=S;s++)
        for(int i=1;i<=cntu;i++){
            if(dp[s][i]==-1||!(s&(1<<(i-1))))
                continue;
            if((s&tar)==tar)
                return true;
            for(int j=1,k;j<=cntu;j++){
                if(s&(1<<(j-1)))
                    continue;
                k=s|(1<<(j-1));
                dp[k][j]=max(dp[k][j],dp[s][i]-w[i][j]);
                if(dp[k][j]>=0&&!(tar&(1<<(j-1))))
                    dp[k][j]=lmt;
            }
        }
    return false;
}
void partition()
{
    int l=0,r=mx,ans=-1;
    while(l<=r){
        int mid=(l+r)/2;
        if(DP(mid))
            r=mid-1,ans=mid;
        else
            l=mid+1;
    }
    printf("%d\n",ans);
}
int main()
{
    while(scanf("%d%d",&n,&m)){
        if(!n&&!m)
            break;
        read();
        if(tag)
            printf("-1\n");
        else
            partition();
    }
}
posted @ 2016-03-20 11:34  KatarinaYuan  阅读(145)  评论(0编辑  收藏  举报