计蒜客模拟赛D2T3 蒜头君救人:用bfs转移状压dp
题目链接:https://nanti.jisuanke.com/t/16444
题意:
蒜头君是一个乐于助人的好孩子,这天他所在的乡村发生了洪水,有多名村民被困于孤岛上,于是蒜头君决定去背他们离开困境,假设蒜头君所在的村子是n*m的网格,网格中.号代表平地,#号代表该地已被洪水淹没,A、B……等大写字母表示该地有村民被困,s代表蒜头君的起点,t代表蒜头君的终点。
蒜头君的初始速度为 k秒一格,他每次可以向上下左右4个方向中的一个移动1格。在背上一个村民后,他的速度可能会降低,也可能会加快,但他的速度不能快于1秒每格,那么蒜头君想知道,他最快需要多长时间将所有村民救出?
注意:不能在终点以外的地方放下村民;可以同时背多个村民。
题解:
用dp[state][x][y]表示现在在(x , y)这个点,村民的状态为state。state是一个三进制数,每一位对应一个村民,0表示村民还在原地,1表示正在背着这个村民,2表示这个村民已经到达终点。
显然,应该先枚举村民状态state,再枚举当前位置(x , y)。但本题和一般的网格dp不同,起点并不一定在左上角(1 , 1)处,而且并不是只能走右和下两个方向,因此本题需要从起点开始bfs,对于每一个被更新dp值的状态,都需要加入队列中。
下面考虑如何转移。
如果当前地点为'.'(平地),那么state不变,向上下左右四个方向转移(不能是'#'),dp[state][...][...](上下左右) = min(dp[state][...][...] , dp[state][x][y] + cal_spd(state)),cal_spd(state)计算的是在当前状态时自己的速度。
如果当前地点为大写字母(有村民要救),并且当前state中这个村民还在原地,那么可以背上这个村民,dp[state_pick][x][y] = min(dp[state_pick][x][y] , dp[state][x][y]),state_pick为在当前state下再背上这个村民时的状态。
如果现在在终点t,很明显只有两种选择,一种是放下所有会让我减速的村民(能让我加速的村民肯定要一直背到底),一种是放下全部的村民(结束救人)。两种转移后的状态分别为state_part和state_all,那么dp[state_part][x][y] = min(dp[state_part][x][y] , dp[state][x][y]),dp[state_all][x][y] = min(dp[state_all][x][y] , dp[state][x][y])。
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <ctype.h> 5 #include <queue> 6 #define MAX_N 15 7 #define MAX_S 60000 8 #define MAX_A 30 9 #define INF 10000000 10 11 using namespace std; 12 13 const int POW[]={1,3,9,27,81,243,729,2187,6561,19683,59049,177147,531441}; 14 15 struct Coor 16 { 17 int x; 18 int y; 19 Coor(int _x,int _y) 20 { 21 x=_x; 22 y=_y; 23 } 24 Coor(){} 25 }; 26 27 struct sta 28 { 29 int state; 30 int x; 31 int y; 32 sta(int _state,int _x,int _y) 33 { 34 state=_state; 35 x=_x; 36 y=_y; 37 } 38 sta(){} 39 }; 40 41 int n,m,k; 42 int cnt=0; 43 int spd[MAX_A]; 44 int dp[MAX_S][MAX_N][MAX_N]; 45 char c[MAX_N][MAX_N]; 46 bool vis[MAX_S][MAX_N][MAX_N]; 47 Coor start; 48 Coor over; 49 queue<sta> q; 50 51 inline int update(int &v,int k,int a) 52 { 53 return v=v-((v/POW[k])%3)*POW[k]+a*POW[k]; 54 } 55 56 inline int query(int v,int k) 57 { 58 return (v/POW[k])%3; 59 } 60 61 void read() 62 { 63 cin>>n>>m>>k; 64 for(int i=1;i<=n;i++) 65 { 66 for(int j=1;j<=m;j++) 67 { 68 cin>>c[i][j]; 69 if(c[i][j]=='s') start=Coor(i,j); 70 if(c[i][j]=='t') over=Coor(i,j); 71 if(isupper(c[i][j])) cnt++; 72 } 73 } 74 for(int i=0;i<cnt;i++) 75 { 76 char ch; 77 int speed; 78 cin>>ch>>speed; 79 spd[i]=speed; 80 } 81 } 82 83 int cal_spd(int state) 84 { 85 int sum=k; 86 for(int i=0;i<cnt;i++) 87 { 88 int val=query(state,i); 89 if(val==1) sum+=spd[i]; 90 } 91 return sum>=1?sum:1; 92 } 93 94 bool is_legal(int x,int y) 95 { 96 return c[x][y]!='#' && x>0 && x<=n && y>0 && y<=m; 97 } 98 99 int set_part(int state) 100 { 101 for(int i=0;i<cnt;i++) 102 { 103 if(spd[i]<=0) continue; 104 int val=query(state,i); 105 if(val==1) update(state,i,2); 106 } 107 return state; 108 } 109 110 int set_all(int state) 111 { 112 for(int i=0;i<cnt;i++) 113 { 114 int val=query(state,i); 115 if(val==1) update(state,i,2); 116 } 117 return state; 118 } 119 120 int pick_up(int state,int k) 121 { 122 return update(state,k,1); 123 } 124 125 void init_dp() 126 { 127 for(int state=0;state<POW[cnt];state++) 128 { 129 for(int i=1;i<=n;i++) 130 { 131 for(int j=1;j<=m;j++) 132 { 133 dp[state][i][j]=INF; 134 } 135 } 136 } 137 dp[0][start.x][start.y]=0; 138 } 139 140 sta get_front() 141 { 142 sta now=q.front(); 143 q.pop(); 144 vis[now.state][now.x][now.y]=false; 145 return now; 146 } 147 148 void insert(sta now) 149 { 150 if(vis[now.state][now.x][now.y]) return; 151 q.push(now); 152 vis[now.state][now.x][now.y]=true; 153 } 154 155 void bfs(sta start) 156 { 157 memset(vis,false,sizeof(vis)); 158 insert(start); 159 while(!q.empty()) 160 { 161 sta now=get_front(); 162 int x=now.x; 163 int y=now.y; 164 int state=now.state; 165 int speed=cal_spd(state); 166 if(is_legal(x+1,y) && dp[state][x+1][y]>dp[state][x][y]+speed) 167 { 168 dp[state][x+1][y]=dp[state][x][y]+speed; 169 insert(sta(state,x+1,y)); 170 } 171 if(is_legal(x-1,y) && dp[state][x-1][y]>dp[state][x][y]+speed) 172 { 173 dp[state][x-1][y]=dp[state][x][y]+speed; 174 insert(sta(state,x-1,y)); 175 } 176 if(is_legal(x,y+1) && dp[state][x][y+1]>dp[state][x][y]+speed) 177 { 178 dp[state][x][y+1]=dp[state][x][y]+speed; 179 insert(sta(state,x,y+1)); 180 } 181 if(is_legal(x,y-1) && dp[state][x][y-1]>dp[state][x][y]+speed) 182 { 183 dp[state][x][y-1]=dp[state][x][y]+speed; 184 insert(sta(state,x,y-1)); 185 } 186 if(c[x][y]=='t') 187 { 188 int state_part=set_part(state); 189 if(dp[state_part][x][y]>dp[state][x][y]) 190 { 191 dp[state_part][x][y]=dp[state][x][y]; 192 insert(sta(state_part,x,y)); 193 } 194 int state_all=set_all(state); 195 if(dp[state_all][x][y]>dp[state][x][y]) 196 { 197 dp[state_all][x][y]=dp[state][x][y]; 198 insert(sta(state_all,x,y)); 199 } 200 } 201 if(isupper(c[x][y])) 202 { 203 int state_pick=pick_up(state,c[x][y]-'A'); 204 if(dp[state_pick][x][y]>dp[state][x][y]) 205 { 206 dp[state_pick][x][y]=dp[state][x][y]; 207 insert(sta(state_pick,x,y)); 208 } 209 } 210 } 211 } 212 213 void solve() 214 { 215 init_dp(); 216 bfs(sta(0,start.x,start.y)); 217 } 218 219 void print() 220 { 221 cout<<dp[POW[cnt]-1][over.x][over.y]<<endl; 222 } 223 224 int main() 225 { 226 read(); 227 solve(); 228 print(); 229 }