【HDU3681】Prison Break-状态压缩DP+BFS+二分答案

测试地址:Prison Break
题目大意:一个n×m的网格中,一些格子是空地,一些格子是开关(一开始状态为开),一些格子是能量池,还有一些格子不能走。要求从一个给定的格子出发,关闭所有的开关,每走一格会耗费一个单位能量,走到能量池可以使用它来回复能量到满电池容量,也可以走过不使用,能量池只能使用一次,问要关闭所有开关所需的最小的电池容量是多少。出发时是满能量的。
做法:本题需要用到状态压缩DP+BFS+二分答案。
首先,注意到电池容量大于某一个阈值时,都是可以完成任务的,反之就都不能完成,答案具有单调性,可以二分。接下来就是判定对于一个给定的电池容量mid,存不存在一种方案使得能开完所有开关。
由于能量池和开关都是只能使用一次(使用多次没有意义),而且它们的数量很少(15个),所以可以套用经典的状压DP模型——TSP(旅行商)问题来解决,只需要BFS预处理出这些点两两之间需要消耗的能量,然后设f(i,j)为节点使用过的状态为i时,最后走到点j的最大剩余能量,然后就可以列出方程计算了,写的时候要注意一些细节,就可以完成这个问题了。
以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,map[21][21],tot,p[21][2],t[21],final,start;
int g[21][21],dis[21][21],f[40010][21];
int q[410][2],head,tail;
bool vis[21][21];
char s[21];
struct forsort {int val,bit;} st[40010];

bool cmp(forsort a,forsort b)
{
    return a.bit<b.bit;
}

void bfs(int s)
{
    head=1,tail=1;
    memset(vis,0,sizeof(vis));
    q[1][0]=p[s][0],q[1][1]=p[s][1];
    dis[q[1][0]][q[1][1]]=0;
    vis[q[1][0]][q[1][1]]=1;
    while(head<=tail)
    {
        int x=q[head][0],y=q[head][1];
        if (x>1&&!map[x-1][y]&&!vis[x-1][y]) dis[x-1][y]=dis[x][y]+1,vis[x-1][y]=1,q[++tail][0]=x-1,q[tail][1]=y;
        if (x<n&&!map[x+1][y]&&!vis[x+1][y]) dis[x+1][y]=dis[x][y]+1,vis[x+1][y]=1,q[++tail][0]=x+1,q[tail][1]=y;
        if (y>1&&!map[x][y-1]&&!vis[x][y-1]) dis[x][y-1]=dis[x][y]+1,vis[x][y-1]=1,q[++tail][0]=x,q[tail][1]=y-1;
        if (y<m&&!map[x][y+1]&&!vis[x][y+1]) dis[x][y+1]=dis[x][y]+1,vis[x][y+1]=1,q[++tail][0]=x,q[tail][1]=y+1;
        head++;
    }
    for(int i=1;i<=tot;i++)
    {
        if (vis[p[i][0]][p[i][1]]) g[s][i]=dis[p[i][0]][p[i][1]];
        else g[s][i]=-1;
    }
}

bool check(int mid)
{
    for(int i=0;i<(1<<tot);i++)
        for(int j=1;j<=tot;j++)
            f[i][j]=-1;
    f[1<<(start-1)][start]=mid;
    for(int i=2;i<=(1<<tot);i++)
    {
        int v=st[i].val,w[21]={0};
        int x=v,j=1;
        while(x)
        {
            if (x&1) w[++w[0]]=j;
            x>>=1;j++;
        }
        for(j=1;j<=w[0];j++)
        {
            for(int k=1;k<=w[0];k++)
                if (k!=j)
                {
                    if (t[w[k]]==1&&g[w[k]][w[j]]!=-1&&f[v-(1<<(w[j]-1))][w[k]]!=-1)
                        f[v][w[j]]=max(f[v][w[j]],mid-g[w[k]][w[j]]);
                    else if (g[w[k]][w[j]]!=-1&&f[v-(1<<(w[j]-1))][w[k]]!=-1)
                        f[v][w[j]]=max(f[v][w[j]],f[v-(1<<(w[j]-1))][w[k]]-g[w[k]][w[j]]);
                }
        }
    }
    for(int i=0;i<(1<<tot);i++)
        if ((i&final)==final)
        {
            for(int j=1;j<=tot;j++)
                if (f[i][j]>=0) return 1;
        }
    return 0;
}

int main()
{
    while(scanf("%d%d",&n,&m)&&n&&m)
    {
        final=0;
        tot=0;
        memset(map,0,sizeof(map));
        for(int i=1;i<=n;i++)
        {
            scanf("%s",s);
            for(int j=1;j<=m;j++)
            {
                if (s[j-1]=='D') map[i][j]=1;
                if (s[j-1]=='F') p[++tot][0]=i,p[tot][1]=j,t[tot]=0,start=tot;
                if (s[j-1]=='G') p[++tot][0]=i,p[tot][1]=j,t[tot]=1;
                if (s[j-1]=='Y') p[++tot][0]=i,p[tot][1]=j,t[tot]=2,final+=1<<(tot-1);
            }
        }

        for(int i=1;i<=tot;i++)
            bfs(i);
        for(int i=0;i<(1<<tot);i++)
        {
            st[i+1].val=i;
            st[i+1].bit=0;
            int x=i;
            while(x)
            {
                if (x&1) st[i+1].bit++;
                x>>=1;
            }
        }
        sort(st+1,st+(1<<tot)+1,cmp);

        int l=0,r=n*m+1;
        while(l<r)
        {
            int mid=(l+r)>>1;
            if (check(mid)) r=mid;
            else l=mid+1;
        }
        if (l!=n*m+1) printf("%d
",l);
        else printf("-1
");
    }

    return 0;
}
原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793584.html