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 }
  • 相关阅读:
    SharePoint 2013 图文开发系列之自定义字段
    SharePoint 2013 图文开发系列之Visual Studio 创建母版页
    SharePoint 2013 图文开发系列之代码定义列表
    SharePoint 2013 图文开发系列之计时器任务
    SharePoint 2013 图文开发系列之应用程序页
    SharePoint 2013 图文开发系列之事件接收器
    SharePoint 2013 图文开发系列之可视化WebPart
    SharePoint 2013 图文开发系列之WebPart
    SharePoint 2013 对二进制大型对象(BLOB)进行爬网
    SharePoint 2013 状态机工作流之日常报销示例
  • 原文地址:https://www.cnblogs.com/acm-bingzi/p/3309095.html
Copyright © 2011-2022 走看看