当前位置:   article > 正文

hdoj-1044 Collect More Jewels 这题对我来说有点难.._hduoj1044、

hduoj1044、

参考博客:http://www.xuebuyuan.com/2176887.html

第一种方法:BFS+状态压缩:(700+ms)

  1. #include<iostream>
  2. #include<cstring>
  3. #include<cstdio>
  4. #include<cmath>
  5. #include<queue>
  6. using namespace std;
  7. const int maxn = 55;
  8. struct node{
  9. int x, y; //坐标位置
  10. int key; //用10位二进制来标识拿到了的珠宝
  11. int t; //已经用的时间
  12. }s, u, v;
  13. char G[maxn][maxn];//地图
  14. int val[11]; //存储珠宝的价值
  15. bool vis[1100][maxn][maxn]; //存储最多十种珠宝所对应的各种状态下的地图,即一维要1<<10,即11位, 用后面十位标识各种状态
  16. int dir[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};//上下左右
  17. int Ex, Ey, W, H, L, M, ans; //终点、宽、高、时间、珠宝数、答案
  18. int counter = 0;
  19. int cal(int key) {//计算当前珠宝状态对应的总价值
  20. int ret = 0;
  21. for(int i = 0, j = 1; i < 10; i++, j *= 2)
  22. if(j&key) ret += val[i];
  23. return ret;
  24. }
  25. bool check(int x, int y) {
  26. return x < H && x >= 0 && y < W && y >= 0; //把这里的W写成了M, 坑了我好长时间
  27. }
  28. void bfs() {
  29. queue<node> que;
  30. que.push(s);
  31. while(!que.empty()) {
  32. u = que.front();
  33. // printf("出来的点 (%d, %d) cal == %d t = %d u.key=%d\n", u.x, u.y, cal(u.key), u.t, u.key);
  34. que.pop();
  35. for(int i = 0; i < 4; i++) {
  36. v.x = u.x + dir[i][0];
  37. v.y = u.y + dir[i][1];
  38. // printf("(%d, %d)\n", v.x, v.y);
  39. if(check(v.x, v.y) && G[v.x][v.y] != '*') {
  40. if(G[v.x][v.y] != '.') v.key = (u.key | (1 << (G[v.x][v.y] - 'A')));//用按位或更新宝物状态
  41. else v.key = u.key;
  42. if(!vis[v.key][v.x][v.y]) { //只访问没有访问过的状态
  43. vis[v.key][v.x][v.y] = true; //状态访问标记更新
  44. v.t = u.t + 1; //时间代价+1
  45. // printf("(%d, %d) cal == %d t = %d\n", v.x, v.y, cal(v.key), v.t);
  46. if(v.x == Ex && v.y == Ey) ans = max(ans, cal(v.key)); //每一次到达终点的时候更新一下获得珠宝的最大值
  47. if(v.t < L) {que.push(v); //如果还没到达要求时间,就继续入队
  48. // cout << "进去的点 :" << v.x << ", " << v.y << endl;
  49. }
  50. }
  51. }
  52. }
  53. }
  54. if(ans == -1) printf("Impossible\n");
  55. else printf("The best score is %d.\n", ans);
  56. }
  57. int main() {
  58. int T; scanf("%d", &T);
  59. int cas = 0;
  60. while(T--) {
  61. scanf("%d%d%d%d", &W, &H, &L, &M);
  62. for(int i = 0; i < M; i++) {
  63. scanf("%d", &val[i]);
  64. }
  65. for(int i = 0; i < H; i++) scanf("%s", G[i]);
  66. for(int i = 0; i < H; i++)
  67. for(int j = 0; j < W; j++){
  68. if(G[i][j] == '@'){
  69. s.x = i; s.y = j;
  70. s.key = 0; s.t = 0;
  71. G[i][j] = '.';
  72. }
  73. else if(G[i][j] == '<') {
  74. Ex = i; Ey = j;
  75. G[i][j] = '.';
  76. }
  77. }
  78. // cout << G[Ex][Ey] << endl;
  79. // cout << s.x << " " << s.y << " " << Ex << " " << Ey << endl;
  80. // for(int i = 0; i < 4; i++) printf("%s\n", G[i]);
  81. //初始化-----------------------
  82. memset(vis, false, sizeof(vis));
  83. vis[0][s.x][s.y] = true;
  84. ans = -1;
  85. printf("Case %d:\n", ++cas);
  86. bfs();
  87. if(T!=0) printf("\n");
  88. }
  89. return 0;
  90. }


将珍宝状态压缩,对于l个珍宝,我们可以用l位二进制表示珍宝的获取情况(1表示已经获得,0表示未获得)。然后我们用vis[i][j][k]表示状态,i表示珍宝获取情况,(j,k)表示位置。这样我们相当于走2^l张地图,每次得到珍宝时转换地图,这样就需要相当多的时间消耗了。


第二种方法:DFS+BFS:(31ms)

  1. #include<iostream>
  2. #include<cstring>
  3. #include<cstdio>
  4. #include<queue>
  5. #include<cmath>
  6. using namespace std;
  7. const int INF = 1e8;
  8. const int maxn = 55;
  9. struct node{
  10. int x, y;
  11. int t;
  12. }s, u, v;
  13. char G[maxn][maxn];
  14. bool vis[maxn][maxn], vis2[12];
  15. int path[12][12], val[12];
  16. int W, H, L, M;
  17. int sum, ans; // 所有珠宝总值, 能获得的最大总值
  18. int dir[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
  19. void bfs(int x, int y, int from) {
  20. memset(vis, false, sizeof(vis));
  21. s.x = x; s.y = y; s.t = 0;
  22. vis[s.x][s.y] = true;
  23. queue<node> que;
  24. que.push(s);
  25. while(!que.empty()) {
  26. u = que.front(); que.pop();
  27. for(int i = 0; i < 4; i++) {
  28. v.x = u.x + dir[i][0];
  29. v.y = u.y + dir[i][1];
  30. if(v.x < 0 || v.x >= H || v.y < 0 || v.y >= W || G[v.x][v.y] == '*' || vis[v.x][v.y]) continue;
  31. vis[v.x][v.y] = true;
  32. v.t = u.t + 1;
  33. if(G[v.x][v.y] != '.') {
  34. if(G[v.x][v.y] == '@') path[from][0] = v.t;
  35. else if(G[v.x][v.y] == '<') path[from][M+1] = v.t;
  36. else path[from][G[v.x][v.y]-'A'+1] = v.t;
  37. }
  38. que.push(v);
  39. }
  40. }
  41. }
  42. void dfs(int cur, int value, int time) { //当前出发位置, 当前得到珠宝价值, 当前已经用了的时间
  43. if(time > L || ans == sum) return ;
  44. if(cur == M+1) { //M+1是终点
  45. ans = max(ans, value); return ;
  46. }
  47. for(int i = 1; i <= M+1; i++) {
  48. if(vis2[i]) continue;
  49. vis2[i] = true;
  50. dfs(i, value+val[i-1], time+path[cur][i]);
  51. vis2[i] = false;
  52. }
  53. }
  54. int main() {
  55. int T; scanf("%d", &T);
  56. int cas = 0;
  57. while(T--) {
  58. sum = 0;
  59. scanf("%d%d%d%d", &W, &H, &L, &M);
  60. for(int i = 0; i < M; i++) {
  61. scanf("%d", &val[i]);
  62. sum += val[i];
  63. }
  64. val[M] = 0;
  65. for(int i = 0; i < H; i++) scanf("%s", G[i]);
  66. for(int i = 0; i <= M+1; i++)
  67. for(int j = 0; j <= M+1; j++)
  68. path[i][j] = INF;
  69. for(int i = 0; i < H; i++)
  70. for(int j = 0; j < W; j++) {
  71. if(G[i][j] == '.' || G[i][j] == '*') continue;
  72. if(G[i][j] == '@') bfs(i, j, 0);
  73. else if(G[i][j] == '<') bfs(i, j, M+1);
  74. else if(G[i][j] <= 'J' && G[i][j] >= 'A') bfs(i, j, G[i][j]-'A'+1);
  75. }
  76. ans = -1;
  77. memset(vis2, false, sizeof(vis2));
  78. dfs(0, 0, 0);
  79. printf("Case %d:\n", ++cas);
  80. if(ans == -1) printf("Impossible\n");
  81. else printf("The best score is %d.\n", ans);
  82. if(T) printf("\n");
  83. }
  84. return 0;
  85. }


珍宝至多10件,我们可以找出起点到所有珍宝和终点的最短路径,珍宝到其他珍宝和终点的最短路径。需要bfs地图至多21次,比上面的2^l次要少很多。我们得到所有的最短路径之后就可以用dfs搜索最大能获得价值了。

提示:可以用sum保存所有珍宝的价值和,要是dfs时,答案ans=sum,那么就可以剪枝掉了,why?因为没有比这个价值更大的了。

注意:我们需要把所有的最短路径path[i][j]初始化为无穷大,因为可能出现走不到的情况。


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/645512
推荐阅读
相关标签
  

闽ICP备14008679号