题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3442
题目大意:三国时期,刘备逃亡。给定一个最大为50*50的地图,刘备在地图中只能往4个方向走。
地图中,A代表瞭望塔,攻击范围是2,攻击伤害是1;
B 代表堡垒,攻击范围是3,攻击伤害是2;
C 代表火焰,对于走在该位置上的单位造成3点伤害;
D 代表弓箭手,攻击范围是2,攻击伤害是4;
E 代表士兵,攻击范围是1,攻击伤害是5;
$ 代表刘备;
! 代表目的地;
# 代表障碍物
. 代表地板
刘备不能穿过A,B,D,E。但是可以走上C和地板。 有3条重要规则:
1.刘备不能被相同的东西伤害2次,比如之前被瞭望塔伤害过,之后再走近瞭望塔的攻击范围时不受伤害。
2.当刘备到达目的地,首先要计算他受到的伤害,然后结束游戏。
3.不需要计算刘备在开始位置受到的伤害。
判断刘备是否可以消耗最少HP,到达目的地,求出最少消耗。
Sample Input
1
4 3
.$.
ACB
ACB
.!.
Sample Output
Case 1: 6
分析:这道题目恶心死我了,之前用map[][],mp[][]两个数组想着方便,结果有一个地方弄错了,那个纠结啊,纠结。看来大牛喜欢长的名字也不是没有道理,起码不会跟其他名称混掉。
题目中的攻击范围(measured by Manhattan distance)是两个点p1(x1,y1),p2(x2,y2)之间,|x1-x2|+|y1-y2|的大小
因为A,B,C,D,E所造成的伤害分别是1,2,3,4,5,而且每种伤害最多只能受一次,所以刘备最多受到1+2+3+4+5点伤害。此时我们可以用二进制中的位数来表示伤害,比如,
01100这个状态就表示刘备收到了C造成的3点伤害和D造成的4点伤害。
这样做的另一个好处就是如果同一个位置受到两种或多种伤害,也可以同时表示出来,互相之间不会干扰。
令dp[i][j][s]表示刘备在(i,j)的位置时,受伤害的状态为s时,HP的最小花费。则答案为终点位置,所有受伤状态里边HP的最小花费。
接下来BFS
代码如下:
1 # include<cstdio> 2 # include<cstring> 3 # include<queue> 4 # include<cmath> 5 using namespace std; 6 const int MAX = 55; 7 char map[MAX][MAX]; 8 int damage[MAX][MAX]; //地图上的每一个位置刘备受到的伤害,如果为-1,表示刘备不能进入该位置 9 int dp[MAX][MAX][1<<5]; 10 int dx[] = {1,0,0,-1}; 11 int dy[] = {0,1,-1,0}; 12 struct node{ 13 int x,y,hp; 14 }st,u,v; 15 16 int n, m, sx, sy, ex, ey; 17 queue<node >q; 18 bool judge(int x,int y){ 19 if(x>=0 && x<n &&y>=0 && y<m && damage[x][y] != 1) 20 return true; 21 return false; 22 } 23 void init(){ //将原地图转化成刘备受伤害的地图 24 int i,j,x,y,bx,by; 25 memset(damage,0,sizeof(damage)); 26 for(i=0; i<n; i++){ 27 for(j=0; j<m; j++){ 28 if(map[i][j] == '#') 29 damage[i][j] = -1; //不可以踏入 30 31 else if(map[i][j] == '$') 32 sx = i, sy = j; 33 34 else if(map[i][j] == '!') 35 ex = i, ey = j; 36 37 else if(map[i][j] == 'A'){ 38 damage[i][j] = -1; 39 for(x=-2; x<3; x++){ 40 for(y=-2; y<3; y++){ 41 if(abs(x) + abs(y) >2) continue; 42 bx = i+x; 43 by = j+y; 44 if(judge(bx,by)) 45 damage[bx][by] |= 1; 46 } 47 } 48 } 49 else if(map[i][j]=='B'){ 50 damage[i][j] = -1; 51 for(x=-3; x<4; x++){ 52 for(y=-3; y<4; y++){ 53 if(abs(x) + abs(y) > 3) continue; 54 bx = i+x; 55 by = j+y; 56 if(judge(bx,by)) 57 damage[bx][by] |= 1<<1; 58 } 59 } 60 } 61 else if(map[i][j] == 'C') 62 damage[i][j] |= 1<<2; 63 64 else if(map[i][j] == 'D'){ 65 damage[i][j] = -1; 66 for(x=-2; x<3; x++){ 67 for(y=-2; y<3; y++){ 68 if(abs(x) + abs(y) >2) continue; 69 bx = i+x; 70 by = j+y; 71 if(judge(bx,by)) 72 damage[bx][by] |= 1<<3; 73 } 74 } 75 } 76 else if(map[i][j] == 'E'){ 77 damage[i][j] = -1; 78 for(x=-1; x<2; x++){ 79 for(y=-1; y<2; y++){ 80 if(abs(x) + abs(y) >1) continue; 81 bx = i+x; 82 by = j+y; 83 if(judge(bx,by)) 84 damage[bx][by] |= 1<<4; 85 } 86 } 87 } 88 89 } 90 } 91 } 92 int main(){ 93 int T,cas; 94 int i,j; 95 scanf("%d",&T); 96 for(cas=1; cas<=T; cas++) 97 { 98 scanf("%d%d",&n,&m); 99 for(i=0; i<n; i++) 100 scanf("%s", map[i]); 101 init(); 102 st.x = sx; 103 st.y = sy; 104 st.hp = 0; 105 memset(dp, -1, sizeof(dp)); 106 dp[st.x][st.y][0] = 0; 107 //以上为初始化 108 q.push(st); 109 int a, b, c; 110 //BFS 111 while(!q.empty()){ 112 u = q.front(); 113 q.pop(); 114 for(i=0; i<4; i++){ 115 b = dp[u.x][u.y][u.hp]; 116 v.x = u.x + dx[i]; 117 v.y = u.y + dy[i]; 118 if(v.x>=n || v.x<0 || v.y>=m || v.y<0 ) continue; //不在地图上 119 if(damage[v.x][v.y] == -1) continue; //不能踏入 120 121 for(j=0; j<5; j++){ 122 a = damage[v.x][v.y] & (1<<j); //此位置是否有给单位的伤害 123 c = u.hp & (1<<j); //刘备是否已经受过了该单位的伤害 124 if(a!=0 && c==0) 125 b += j+1; //刘备受到伤害,伤害值为j+1 126 } 127 v.hp = u.hp | damage[v.x][v.y]; //经过该点受到的总伤害 128 if(dp[v.x][v.y][v.hp] == -1 || dp[v.x][v.y][v.hp] > b){ 129 dp[v.x][v.y][v.hp] = b; 130 q.push(v); 131 } 132 } 133 134 } 135 int ans = -1; 136 for(i=0; i< (1<<5); i++){ 137 if(dp[ex][ey][i] != -1 && (dp[ex][ey][i]<ans || ans==-1)) 138 ans = dp[ex][ey][i]; 139 } 140 printf("Case %d: %d ",cas,ans); 141 } 142 return 0; 143 }