zoukankan      html  css  js  c++  java
  • HDU 3681 Prison Break(状态压缩dp + BFS)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3681

    前些天花时间看到的题目,但写出不来,弱弱的放弃了。没想到现在学弟居然写出这种代码来,大吃一惊附加敬仰之情。这里借用下他的成果,好好学习吧,骚年***

    Sample Input
    5 5
    GDDSS
    SSSFS
    SYGYS
    SGSYS
    SSYSS
    0 0
     
    Sample Output
    4

    题意:给出矩阵(作为监狱)和在监狱中的一个装有电池的机器人,其中

    • F为出发点,图中只有一个,且初始状态下机器人在此处电池为满电量;
    • D为障碍物,不可走;
    • S为空格子,机器可自由通过;
    • G为充电点,只能充电一次,且一次能充满电池,经过G可作为S不充电,充电后G变为S;
    • Y点为电闸,机器人可通过,且通过后变为S。

    机器人通过所有Y后才能飞出监狱够逃走。在此之前他只能一个格子一个格子(向上、向下、向左、向右)地走动,而且每走一格消耗一格电池的电量,如果电池没电他就不能移动。问他的电池满电量状态下至少有几格电量才能使他逃出监狱?

    该题的思路来源于它的数据,Y+G的数量小于15。所以从这里就可以考虑状态压缩DP。

    电量求法采用的是二分查找。

    整幅图显然要进行处理,Y,F都是必须要互相连通的,若不连通,显然是无解,反之必然有解。将Y,F,G对应的编号,起点F定位0,Y,G依次编号,将Y和G分成两块编号,那么接下来对于电闸Y和充电点G的判断就会比较容易,只要直接判断编号范围即可。

    接下来就是求对于每个点的最短路了,再求完最短路后,如发现Y,F间某2点无法连通,则无解,否则有解。

    若有解,则进行状态压缩DP,对于每一个maxt(当前的电池满格电量),途中大于maxt的,则不能更新,反正则可以进行判断并更新。若每个Y点都已经遍历过,则判断该状态下是否可行,可行则返回TRUE。

    代码如下:

      1 #include<algorithm>
      2 #include<iostream>
      3 #include<cstring>
      4 #include<cstdio>
      5 #include<queue>
      6 using namespace std;
      7 const int INF=1<<29;
      8 int as[600000];
      9 void st()
     10 {
     11     int i, k=0;
     12     for(i=1; i<600000; i*=2)
     13     {
     14         as[i]=++k;
     15     }
     16 }
     17 struct Node
     18 {
     19     int x, y;
     20 } nod[17];
     21 struct POS
     22 {
     23     POS() {}
     24     POS(int a,int b,int c)
     25     {
     26         x=a,y=b,now=c;
     27     }
     28     int x, y, now;
     29 } pos;
     30 int n, m, p, q, sx, sy;
     31 int dis[17][17], dp[600000][17], the[17][17], num, ll;
     32 char map[17][17];
     33 bool ans;
     34 void init()            //对每个Y,F,G编号
     35 {
     36     int i, j;
     37     memset(the,-1,sizeof(the));
     38     p=1;
     39     for(i=0; i<n; i++)
     40     {
     41         for(j=0; j<m; j++)
     42         {
     43             if(map[i][j]=='F')
     44                 sx=i, sy=j;
     45             if(map[i][j]=='Y')
     46             {
     47                 nod[p].x=i, nod[p].y=j;
     48                 the[i][j]=p;   //该数组可用于判断某个坐标的点的编号
     49                 p++;
     50             }
     51         }
     52     }
     53     q=p;
     54     for(i=0; i<n; i++)
     55         for(j=0; j<m; j++)
     56         {
     57             if(map[i][j]=='G')
     58             {
     59                 nod[q].x=i, nod[q].y=j;
     60                 the[i][j]=q;
     61                 q++;
     62             }
     63         }
     64     nod[0].x=sx, nod[0].y=sy;
     65     the[sx][sy]=0;
     66     for(i=0; i<q; i++)
     67         for(j=0; j<q; j++)
     68         {
     69             if(i==j)
     70                 dis[i][j]=0;
     71             else dis[i][j]=INF;
     72         }
     73 }
     74 int dx[]= {1,0,-1,0};
     75 int dy[]= {0,1,0,-1};
     76 bool can(int x,int y)
     77 {
     78     if(x<0||x>=n||y<0||y>=m||map[x][y]=='D')
     79         return false;
     80     return true;
     81 }
     82 void bfs()        //BFS求Y,F,G之间的最短路
     83 {
     84     int i, j, nx, ny, x, y, now;
     85     POS tmp;
     86     bool vis[17][17];
     87     for(i=0; i<q; i++)
     88     {
     89         memset(vis,0,sizeof(vis));
     90         queue<POS> qq;
     91         qq.push(POS(nod[i].x,nod[i].y,0));
     92         while(!qq.empty())
     93         {
     94             tmp=qq.front();
     95             qq.pop();
     96             x=tmp.x, y=tmp.y, now=tmp.now;
     97             for(j=0; j<4; j++)
     98             {
     99                 nx=x+dx[j], ny=y+dy[j];
    100                 if(can(nx,ny)&&!vis[nx][ny])
    101                 {
    102                     if(the[nx][ny]!=-1)
    103                         dis[i][the[nx][ny]]=now+1;
    104                     vis[nx][ny]=true;
    105                     qq.push(POS(nx,ny,now+1));
    106                 }
    107             }
    108         }
    109     }
    110     for(i=0; i<p; i++)
    111         for(j=0; j<p; j++)
    112             if(dis[i][j]==INF) ans=false;
    113 }
    114 bool deal(int maxt)        //状态压缩DP判断是否可行
    115 {
    116     int i, j, k, tmp;
    117     for(i=0; i<num; i++)
    118         for(j=0; j<q; j++)
    119             dp[i][j]=INF;
    120     dp[0][0]=0;
    121     int ed;
    122     for(i=1; i<num; i++)
    123     {
    124         for(j=i; j>0; j-=j&(-j))
    125         {
    126             tmp=j&(-j);
    127             ed=as[tmp];
    128             for(k=0; k<q; k++)
    129                 if(dp[i^tmp][k]+dis[k][ed]<=maxt)
    130                     dp[i][ed]=min(dp[i^tmp][k]+dis[k][ed],dp[i][ed]);
    131             if(ed>=p&&dp[i][ed]!=INF)
    132                 dp[i][ed]=0;
    133             if(dp[i][ed]!=INF)
    134             {
    135                 for(k=0; k<p; k++)
    136                 {
    137                     if(dp[i][ed]+dis[ed][k]<=maxt)
    138                         dp[i|(1<<(k-1))][k]=min(dp[i|(1<<(k-1))][k],dp[i][ed]+dis[ed][k]);
    139                 }
    140                 for(; k<q; k++)
    141                 {
    142                     if((1<<(k-1))&i) continue;
    143                     if(dp[i][ed]+dis[ed][k]<=maxt)
    144                         dp[i|(1<<(k-1))][k]=0;
    145                 }
    146             }
    147         }
    148         if(i%ll==ll-1)
    149         {
    150             for(j=0; j<q; j++)
    151                 if(dp[i][j]<=maxt) return true;
    152         }
    153     }
    154     return false;
    155 }
    156 int main()
    157 {
    158     int i, j, tmp, l, r;
    159     st();
    160     while(scanf("%d%d",&n,&m)!=EOF)
    161     {
    162         if(n==0&&m==0) break;
    163         for(i=0; i<n; i++)
    164             scanf("%s",map[i]);
    165         init();
    166         ans=true;
    167         bfs();
    168         num=1<<(q-1);    //所有状态个数
    169         l=1, r=300;
    170         ll=1<<(p-1);
    171         if(ans)            //ans为true表示,Y和F间两两都是连通的
    172         {
    173             while(l<r)            //二分求解
    174             {
    175                 m=(l+r)>>1;
    176                 if(deal(m))
    177                     r=m;
    178                 else l=m+1;
    179             }
    180         }
    181         if(ans) printf("%d
    ",r);
    182         else puts("-1");
    183     }
    184     return 0;
    185 }
  • 相关阅读:
    javascript framework js常用框架
    快速排序Quick sort
    归并排序
    Linux中 设置apache,mysql 开机启动
    Linux下设置mysql和tomcat开机启动
    linux命令之ifconfig详细解释
    CentOS网络接口配置文件ifcfg-eth详解
    条件测试操作与流程控制语句
    从键盘或文件中获取标准输入:read命令
    linux yum命令详解
  • 原文地址:https://www.cnblogs.com/acm-bingzi/p/3309095.html
Copyright © 2011-2022 走看看