题目来源:杭电OJ-1044

题目大意:一个探险家身处一个危险的地下城,城中很危险,并且城中分散着若干个珠宝;现在地下城即将塌陷,冒险家需要在有限的时间内逃出去,但他希望在逃生的过程中获取一些珠宝并使价值最大化。题目要求用程序计算出最优方案所能获取的珠宝价值。

以前也遇到过相似的题目,但一直未真正搞明白,最近再次遇到,阅读了一些大牛的博客,结合自己的理解,将这个知识点记录一下。

这道题之所以不能简单的使用DFS或BFS算法解决,是因为探险家为了获取更多价值,可以走重复路径。最近学习并实现了这道题的两种解决思路,一种是BFS Plus,另一种是BFS+DFS。

一.BFS Plus方案

经典的BFS算法用一个二维数组保存地图的访问状况(可阅读我上一篇转载的文章,讲解的很深刻),而这一题的不同在于每次获取一个珠宝后,可以走重复路径,为了解决这个问题,我们可以在每次获取一个珠宝之后用一个新的二位数组保存后面遍历的访问情况。本方案使用二进制中的一位来标识某个珠宝是否获得,已获得取1,否则取0,这样就可以用一个值来表示珠宝的获取状态,比如说有四个珠宝A,B,C,D,那么我们可以用四位二进制数来表示获取状态,如果已经获取A和D,那么状态值就为9(二进制形式为1001)。本方案就是使用这个状态值来表示不同的二位访问状态数组,所以总体来说需要一个三维的数组来记录访问信息。而本题中最多有10个珠宝,所以需要十位二进制数来标识珠宝的访问状态,故状态数组的第一维为1024。

接下来给出该方案的源代码:

#include <iostream>
#include <queue>
#include <algorithm>
#include <string.h>
using namespace std;int W, H, L, M;
char dun[55][55]; //保存地下城地图
int jew[10]; //保存每个珠宝的价值
bool isVisited[1024][55][55]; //记录BFS过程中某个位置是否经过
//结构体保存每到一个位置时的信息
struct State
{int J, x, y;int jews, time;
};
int dx[] = { -1, 0, 1, 0 };
int dy[] = { 0, 1, 0, -1 };int CollectJewels()
{memset(isVisited, 0, sizeof(isVisited));State begin, pos, nextpos;for (int i = 0; i < H; i++){int j;for (j = 0; j < W; j++)if (dun[i][j] == '@'){begin.x = i;begin.y = j;break;}if (j != W) break;}begin.J = begin.jews = begin.time = 0;isVisited[0][begin.x][begin.y] = true;queue<State> Q;Q.push(begin);int res = -1;while (!Q.empty()){pos = Q.front();Q.pop();if (pos.time == L) break; //优化操作,如果当前位置的时间已经达到//L,那么队列后面位置的时间一定>=L,可终止遍历int i;for (i = 0; i < 4; i++){nextpos = pos;nextpos.x += dx[i];nextpos.y += dy[i];nextpos.time++;if(nextpos.x<0 || nextpos.x>=H ||nextpos.y<0 || nextpos.y>=W) continue;char ch = dun[nextpos.x][nextpos.y];if (ch == '*' || isVisited[nextpos.J][nextpos.x][nextpos.y])continue;isVisited[nextpos.J][nextpos.x][nextpos.y] = true;if (ch >= 'A' && ch <= 'J'){int t = ch - 'A';//如果当前位置的珠宝还在,取之if (!(nextpos.J & (1 << t))){nextpos.jews += jew[t];nextpos.J |= (1 << t);isVisited[nextpos.J][nextpos.x][nextpos.y] = true;}}else if (ch == '<'){res = max(res, nextpos.jews);//优化操作,如果某种方案可以取得城内所有珠宝,则可以停止//尝试其他方案if (nextpos.J == ((1 << M) - 1)) break;else continue;}Q.push(nextpos);}if(i!=4) break;}return res;
}int main() {int T;cin >> T;for (int i = 1; i <= T; i++){cin >> W >> H >> L >> M;for (int j = 0; j < M; j++)cin >> jew[j];for (int j = 0; j < H; j++)cin >> dun[j];int res = CollectJewels();cout << "Case " << i << ":\n";if (res == -1) cout << "Impossible\n";else cout << "The best score is " << res << ".\n";if (i != T) cout << endl;}return 0;
}
二.BFS+DFS方案
本方案先进行BFS操作,计算出每个珠宝以及起点和出口两两之间的距离(也即时间),然后将这些位置作为结点,将距离作为边的权值,构造出一个虚拟图;接下来使用DFS算法计算出图中从起点到出口的最优路径。这种方案相对于上一方案来说更容易理解。
接下来给出该方案的源代码:
#include <iostream>
#include <queue>
#include <string.h>
using namespace std;int W, H, L, M, ans, sum;
char dun[55][55]; //保存地下城地图信息
int jew[15]; //保存每个珠宝的价值
bool isVisited[55][55]; //记录BFS过程中地图的访问状态
int Graph[15][15]; //保存BFS过程中构造的虚拟图的信息
bool isUsed[15]; //记录DFS过程中图中节点的访问状态struct State
{int x, y;int time;State(int a, int b, int c) :x(a), y(b), time(c){}
};
int dx[] = { -1, 0, 1, 0 };
int dy[] = { 0, 1, 0, -1 };void BFS(int x, int y)
{memset(isVisited, 0, sizeof(isVisited));char ch = dun[x][y];int u;if(ch=='@') u=0;else if (ch >= 'A') u = ch - 'A' + 1;State pos(0, 0, 0), nextpos(0, 0, 0);queue<State> Q;isVisited[x][y] = true;Q.push(State(x, y, 0));while (!Q.empty()){pos = Q.front();Q.pop();if (pos.time == L) break; //优化操作for (int i = 0; i < 4; i++){nextpos.x = pos.x + dx[i];nextpos.y = pos.y + dy[i];nextpos.time = pos.time + 1;if (nextpos.x < 0 || nextpos.x >= H ||nextpos.y < 0 || nextpos.y >= W) continue;if (dun[nextpos.x][nextpos.y] == '*' || isVisited[nextpos.x][nextpos.y])continue;isVisited[nextpos.x][nextpos.y] = true;char ch = dun[nextpos.x][nextpos.y];if (ch == '@'){Graph[u][0] = Graph[0][u] = nextpos.time;}else if (ch >= 'A' && ch <= 'J'){int v = ch - 'A' + 1;Graph[u][v] = Graph[v][u] = nextpos.time;}else if (ch == '<'){Graph[u][M + 1] = Graph[M + 1][u] = nextpos.time;}Q.push(nextpos);}}
}void DFS(int pos, int Time, int je)
{if (Time > L) return;if(ans==sum) return; //剪枝操作if (pos == M + 1){if (je > ans) ans = je;return;}for (int i = 1; i < M + 2; i++){if (isUsed[i] || Graph[pos][i] == 0) continue;isUsed[i] = true;DFS(i, Time + Graph[pos][i], je + jew[i]);isUsed[i] = false;}
}
int main()
{int T;cin >> T;for (int i = 1; i <= T; i++){cin >> W >> H >> L >> M;sum=0;for (int j = 1; j <= M; j++){cin >> jew[j];sum+=jew[j];}//把起始位置和出口记作价值为0的位置,分别用0和M+1标识jew[0]=jew[M+1]=0;for (int i = 0; i < H; i++)cin >> dun[i];memset(Graph,0,sizeof(Graph));//BFS过程中构造虚图for (int i = 0; i < H;i++)for (int j = 0; j < W; j++){if (dun[i][j] == '@' || (dun[i][j] >= 'A' && dun[i][j] <= 'J'))BFS(i, j);}memset(isUsed, 0, sizeof(isUsed));isUsed[0]=true;ans = -1;//DFS在途中寻找一条最优路径DFS(0, 0, 0);cout<<"Case "<<i<<":\n";if (ans == -1) cout << "Impossible\n";else cout << "The best score is " << ans << ".\n";if (i != T) cout << endl;}return 0;
}
两方案比较:可以很容易看出,方案一占用的空间较大,效率方面方案一在杭电OJ上运行,耗时390MS,占用空间5784K;而方案二耗时46MS,占用空间1624K。总的来说方案二更胜一筹。

地下城夺宝游戏——杭电OJ 1044题解析相关推荐

  1. 杭电oj刷题2022

    Problem Description potato老师虽然很喜欢教书,但是迫于生活压力,不得不想办法在业余时间挣点外快以养家糊口. "做什么比较挣钱呢?筛沙子没力气,看大门又不够帅...& ...

  2. 杭电oj刷题C语言答案+思路

    作为接触C语言不久的新人来说,确实有很多没想到的地方,不少方法是暴力求解,代码量长,方法麻烦,一些题目还是有借鉴大佬的答案,请多谅解.一些题目有我的笔记.如有错误以及更好的见解,请理性讨论.如果对你有 ...

  3. 杭电Oj刷题(2017)

    字符串统计 题目描述: 对于给定的一个字符串,统计其中数字字符出现的次数. Input 输入数据有多行,第一行是一个整数n,表示测试实例的个数,后面跟着n行,每行包括一个由字母和数字组成的字符串. O ...

  4. 记录小白杭电OJ刷题

    第一阶段:开始入门吧!(15天,53题) 一.输入输出练习(2天,10题) 1000.1089-1096.1001 二.简单操作:(2-4天,12题) 2000-2011.2039 三.英文题试水(3 ...

  5. 杭电Oj刷题(2050)

    折线分割平面 题目描述: 我们看到过很多直线分割平面的题目,今天的这个题目稍微有些变化,我们要求的是n条折线分割平面的最大数目.比如,一条折线可以将平面分成两部分,两条折线最多可以将平面分成7部分,具 ...

  6. 杭电OJ分类题目(1)

    原题出处:HDOJ Problem Index by Type,http://acm.hdu.edu.cn/typeclass.php 杭电OJ分类题目(1) HDU Introduction HDU ...

  7. 杭电OJ第11页2035~2039算法题(C语言)

    目录 2035.人见人爱A^B 2036.改革春风吹满地 2037.今年暑假不AC 2038.Message 2039.三角形 2035.人见人爱A^B Problem Description 求A^ ...

  8. 【ACM】杭电OJ 2037

    题目链接:杭电OJ 2037 先把b[i]进行排序,然后,b[i]与a[i+1]进行比较. #include <iostream> #include <cstdio> #inc ...

  9. 【ACM】杭电OJ 2020(排序)

    题目链接:杭电OJ 2020 排序可以有冒泡排序,选择排序,或者直接调用函数. 下面是选择排序: #include <stdio.h> #include <math.h> in ...

最新文章

  1. SLAM学习--------相机位姿表示-李群李代数
  2. centos 光盘 mysql_Centos6.5 使用光盘镜像系统源安装数据库mysql5.7
  3. async 队列性能测试
  4. Centos7 Docker Jenkins ASP.NET Core 2.0 自动化发布和部署
  5. SMW0 HTML模版的形式上传文件 维护MIME类型
  6. Spring 中常用的设计模式对比
  7. 图象关于y轴对称是什么意思_数学概念丨“图象”与“图像”是有区别的 ,你知道吗?...
  8. redis学习与入门~~~
  9. Android下拉状态栏图标错了,小米手机怎么改状态栏的图标,并且把下拉的状态栏弄透明...
  10. 蔚来ES8正式交付售价46.8万元起 与Model系相比扛打吗?
  11. 电脑耳机声音小怎么调大_湘乡电脑耳机怎么样,IP话机推荐
  12. 二十五、JAVA多线程(一、理论知识)
  13. jdbc 生成建表语句_记录一次TDH的inceptor导出建表语句和数据
  14. 极限精简服务器系统,极限精简斐讯T1/N1 极客开发者强迫症福音6.25
  15. 基于WIKI中文语料·Word2Vec模型训练·Python
  16. 怎么导出微信聊天的记录到Word
  17. kubernetes 创建pod /merged/dev/shm: invalid argument
  18. 面转栅格之ERROR 999999:执行函数时出错
  19. DLL和EXE加载包在自身内部的资源文件
  20. 111111111111

热门文章

  1. JSP页面查询显示常用模式
  2. 服务器托管和虚拟主机区别,虚拟主机和托管主机的区别是什么
  3. Swift - JSON
  4. 揭秘MOS管开关时米勒效应的详情
  5. FotoMagico 5.6.5 特别版 Mac 专业的幻灯片制作工具
  6. IIS下防止mdb数据库被下载的实现方法
  7. 域名、主机和网站之间的区别
  8. 合肥八中2021年高考成绩查询,合肥八中2019高考成绩喜报、一本二本上线情况
  9. 海康工业相机排雷笔记-01-分辨率设置
  10. 对提升仪表自控率的几点建议