蒜头君救人
蒜头君是一个乐于助人的好孩子,这天他所在的乡村发生了洪水,有多名村民被困于孤岛上,于是蒜头君决定去背他们离开困境,假设蒜头君所在的村子是 n \times mn×m 的网格,网格中.
号代表平地,#
号代表该地已被洪水淹没,A
、B
……等大写字母表示该地有村民被困,s
代表蒜头君的起点,t
代表蒜头君的终点。
蒜头君的初始速度为 kk 秒一格,他每次可以向上下左右 44 个方向中的一个移动 11 格。在背上一个村民后,他的速度可能会降低,也可能会加快,但他的速度不能快于 11 秒每格,那么蒜头君想知道,他最快需要多长时间将所有村民救出?
注意:不能在终点以外的地方放下村民;可以同时背多个村民。
输入格式
第一行 33 个正整数 n,m,kn,m,k,分别表示村庄长度、宽度、蒜头君初始速度。
接下来 nn 行,每行一个长度为 mm 的字符串,表示村庄的地形,字符串意义如上所述。
接下来若干行,每行一个大写字母、一个整数,表示该编号的村民会使 kk 增加 / 减少多少。行数等同于地形中大写字母的个数。大写字母按字典序,即A
、B
、C
的顺序排列,保证前后两行的字母是连续的。
输出格式
输出 11 个整数,表示最小用时。
数据规模
对于 10% 的数据,满足 1 \le n,m \le 51≤n,m≤5,村民个数为 11;
对于 50% 的数据,满足 1\le n,m\le51≤n,m≤5,村民个数小于等于 55;
对于 100% 的数据,满足 1\le n,m\le101≤n,m≤10,村民个数小于等于 1010。
样例输入
4 4 2 s.## ..A# .B## ...t A -3 B 4
样例输出
17
题解:dp
首先是50分做法:
定义f[2^num][2^num][x][y]
第一维每一位若为 1 表示该村民正被蒜头君所背,第二维每一位若为1表示该村民已被放至安全地带,
最后两维表示蒜头君当前所处位置。
转移时分别考虑是否有村民,是否是终点就行了
复杂度为O(4^num*n*m)
100分:
将一维和二维合起来,定义一个三进制数,0表示还没救,1表示背在身上,2表示已送达
转移用记忆化搜索就行了,不用考虑转移顺序,但YZD大神直接dp,效率贼高
在到终点时,不用枚举子集,直接考虑2种情况:
1.把会减慢速度的人放下
2.把所有人放下
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 struct Node 7 { 8 int x,y,s; 9 }q[2000001]; 10 int dx[5]={0,1,-1,0,0},dy[5]={0,0,0,1,-1}; 11 int peo[11],k,n,m,t,sp[11],pw[11]; 12 int f[60001][11][11]; 13 char a[11][11],s[11][11]; 14 int cal_spd(int x) 15 {int p=0,ss=0,i; 16 memset(peo,0,sizeof(peo)); 17 while (x) 18 { 19 p++; 20 peo[p]=x%3; 21 x/=3; 22 } 23 ss=k; 24 for (i=1;i<=t;i++) 25 { 26 if (peo[i]==1) 27 { 28 ss+=sp[i]; 29 } 30 } 31 if (ss<1) ss=1; 32 return ss; 33 } 34 int pick(int ss,int x,int y) 35 {int i,po,p=0; 36 for (i=1;i<=t;i++) 37 if (a[x][y]==s[i][0]) 38 { 39 po=i;break; 40 } 41 memset(peo,0,sizeof(peo)); 42 while (ss) 43 { 44 p++; 45 peo[p]=ss%3; 46 ss/=3; 47 } 48 for (i=1;i<=t;i++) 49 if (peo[po]) return 0; 50 peo[po]=1; 51 for (i=t;i>=1;i--) 52 ss=ss*3+peo[i]; 53 return ss; 54 } 55 int drop1(int ss,int x,int y) 56 {int p=0,i; 57 memset(peo,0,sizeof(peo)); 58 while (ss) 59 { 60 p++; 61 peo[p]=ss%3; 62 ss/=3; 63 } 64 for (i=1;i<=t;i++) 65 if (peo[i]==1) peo[i]=2; 66 for (i=t;i>=1;i--) 67 ss=ss*3+peo[i]; 68 return ss; 69 } 70 int drop2(int ss,int x,int y) 71 {int p=0,i; 72 memset(peo,0,sizeof(peo)); 73 while (ss) 74 { 75 p++; 76 peo[p]=ss%3; 77 ss/=3; 78 } 79 for (i=1;i<=t;i++) 80 if (peo[i]==1&&sp[i]>0) peo[i]=2; 81 for (i=t;i>=1;i--) 82 ss=ss*3+peo[i]; 83 return ss; 84 } 85 int main() 86 {int i,j,xh,yh,xt,yt,head,tail; 87 scanf("%d%d%d",&n,&m,&k); 88 for (i=1;i<=n;i++) 89 { 90 scanf("%s",a[i]+1); 91 for (j=1;j<=m;j++) 92 if (a[i][j]=='s') xh=i,yh=j; 93 else if (a[i][j]=='t') xt=i,yt=j; 94 else 95 if (a[i][j]>='A'&&a[i][j]<='Z') t++; 96 } 97 for (i=1;i<=t;i++) 98 { 99 scanf("%s%d",s[i],&sp[i]); 100 } 101 pw[0]=1; 102 for (i=1;i<=t;i++) 103 pw[i]=pw[i-1]*3; 104 memset(f,127/3,sizeof(f)); 105 f[0][xh][yh]=0; 106 q[1].x=xh;q[1].y=yh;q[1].s=0; 107 head=0;tail=1; 108 while (head<tail) 109 { 110 head++; 111 head%=2000000; 112 int spd=cal_spd(q[head].s); 113 int nx=q[head].x,ny=q[head].y; 114 for (i=1;i<=4;i++) 115 { 116 int x=nx+dx[i],y=ny+dy[i]; 117 if (x>=1&&x<=n&&y>=1&&y<=m) 118 { 119 if (a[x][y]!='#'&&f[q[head].s][x][y]>f[q[head].s][nx][ny]+spd) 120 { 121 f[q[head].s][x][y]=f[q[head].s][nx][ny]+spd; 122 tail++;tail%=2000000; 123 q[tail].s=q[head].s;q[tail].x=x;q[tail].y=y; 124 } 125 if (a[x][y]>='A'&&a[x][y]<='Z') 126 { 127 int nxt=pick(q[head].s,x,y); 128 if (f[nxt][x][y]>f[q[head].s][nx][ny]+spd) 129 { 130 f[nxt][x][y]=f[q[head].s][nx][ny]+spd; 131 tail++;tail%=2000000; 132 q[tail].s=nxt;q[tail].x=x;q[tail].y=y; 133 } 134 } 135 if (a[x][y]=='t') 136 { 137 int nxt1=drop1(q[head].s,x,y); 138 int nxt2=drop2(q[head].s,x,y); 139 if (f[nxt1][x][y]>f[q[head].s][x][y]) 140 { 141 f[nxt1][x][y]=f[q[head].s][x][y]; 142 tail++;tail%=2000000; 143 q[tail].s=nxt1;q[tail].x=x;q[tail].y=y; 144 } 145 if (f[nxt2][x][y]>f[q[head].s][x][y]) 146 { 147 f[nxt2][x][y]=f[q[head].s][x][y]; 148 tail++;tail%=2000000; 149 q[tail].s=nxt2;q[tail].x=x;q[tail].y=y; 150 } 151 } 152 } 153 } 154 } 155 cout<<f[pw[t]-1][xt][yt]; 156 }