题目链接: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 }