zoukankan      html  css  js  c++  java
  • 【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;
    }
  • 相关阅读:
    进程之管道Pipe,数据共享Manager,进程池Poo
    进程之锁,信息量,事件,队列,生产者消费者模型,joinablequeue
    网络编程之sock server,自定义一个与sock server类相似的功能,支持多客户端通信
    网络编程之粘包解决方案
    进程之进程创建的两种方式,两种传值的方式,验证进程间数据隔离,join,守护进程,僵尸进程,孤儿进程
    毕业设计开题报告任务书参考文献格式和数量要求
    剑指offer-替换空格
    剑指offer-二维数组中的查找
    用forward和sendRedirect转发时的区别
    ServletContext实现显示用户在线人数
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793584.html
Copyright © 2011-2022 走看看