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\n",r);
182         else puts("-1");
183     }
184     return 0;
185 }

 

你可能感兴趣的:(break)