使用C++定向越野地图游戏
Orienteering map game using C++
我使用蛮力方法解决了编程难题,没有动态编程,它工作得很好。 这是谜题:
定向越野地图将按以下格式给出。
" ##### " " #...# " " #S#G# " " ##### "
通过所有检查点计算从起点到目标的最小距离。
- 地图由 5 个字符组成,如下所示。您可以假设地图不包含任何无效字符,并且地图只有一个起始符号"S"和一个目标符号"G"。
- "S"表示定向越野开始。
- "G"表示定向越野目标。
- "@"表示定向越野检查站。
- "."表示玩家可以通过的开放方块。
- "#"表示玩家无法通过的封闭式阻止。
- 它只允许垂直或水平(向上、向下、向左或向右(移动一步到下一个块。其他类型的移动,例如对角线移动(左上、右上、左下和右下(和跳过一个或多个块,是不允许的。
- 您一定不能离开地图。
- 距离应定义为到不同块的移动次数。
- 如有必要,您可以多次通过打开的块、检查点、起点和目标。
- 您可以假设参数满足以下条件。
- 1 <= 宽度 <= 100
- 1 <= 高度 <= 100 最大检查点数为 18。
然后我找到了一个更快的解决方案,我不明白一些事情:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<cstring>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<cstdlib>
#include<ctime>
#include<set>
#include<math.h>
using namespace std;
typedef long long LL;
const int maxn = 1e2+ 10;
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define pb push_back
std::vector<int>path;
const int INF=1<<20;
struct Point
{
int x,y;
bool operator < (const Point &a)const
{
return x<a.x||(x==a.x)&&y<a.y;
}
};
std::vector<Point>P;
char mat[maxn][maxn];
int vis[maxn][maxn];
int w,h,s,e;
int d[1<<20][20];
int dx[]={-1,0,0,1};
int dy[]={0,-1,1,0};
int dist[25][25];
int main(){
ios_base::sync_with_stdio(false);
cin.tie(0);
while(cin>>w>>h){
map<Point,int>id;
P.clear();
path.clear();
memset(d,100,sizeof d);
memset(dist,100,sizeof dist);
for(int i=0;i<h;i++){
scanf("%s",mat[i]);
for(int j=0;mat[i][j];++j){
char &c=mat[i][j];
if(c=='S'||c=='G'||c=='@'){
P.pb((Point){i,j});
int sz=P.size();
id[P[sz-1]]=sz;
if(c=='S')s=sz-1;
else if(c=='G')e=sz-1;
path.pb(sz-1);
}
}
}
for(int i=0;i<path.size();i++){
Point now=P[path[i]];
int x=path[i];
//out<<"x "<<x<<endl;
dist[x][x]=0;
memset(vis,0,sizeof vis);
vis[now.x][now.y]=1;
queue<Point>q;
q.push(now);
//cout<<"Bfs"<<endl;
while(!q.empty()){
now=q.front();q.pop();
for(int i=0;i<4;i++){
int nx=now.x+dx[i],ny=now.y+dy[i];
if(nx>=0&&nx<h&&ny>=0&&ny<w&&mat[nx][ny]!='#'&&!vis[nx][ny]){
Point tp=(Point){nx,ny};
q.push(tp);
vis[nx][ny]=vis[now.x][now.y]+1;
if(id[tp]){
dist[x][id[tp]-1]=vis[now.x][now.y];
}
}
}
}
}
d[1<<s][s]=0;
int M=path.size();
for(int i=0;i<(1<<M);++i){
for(int j=0;j<M;j++){
int p=path[j];
for(int k=0;1<<k<=i;k++){
if(i&(1<<k)){
d[i|(1<<p)][p]=min(d[i|(1<<p)][p],d[i][k]+dist[k][p]);
}
}
}
}
cout<<d[(1<<M)-1][e]<<endl;
}
return 0;
}
以下是我对此的 3 个具体问题:
常数
INF
有什么用?它不会在程序的任何地方使用。我知道程序员经常在他们的程序中留下一些东西,这些东西目前似乎没有任何用处,但对未来的任何修改都是有用的。INF是否具有相同的目的?如果执行任何类型的修改以使程序更有效或使用不同的方法,则使用INF
?在数组维度中使用左移运算符。例如,
int d[1<<20][20]
.让移运算符在这个程序中有什么目的?还有其他各种实例在数组维度中使用了 let shift 运算符,我不明白为什么。小于运算符的重载。在
Point
结构中,小于运算符过载。但我似乎找不到它在程序中的哪个位置被调用。它需要一个 Point 对象来调用它,但我找不到任何 Point 对象调用该成员函数的地方。
您的问题不是无效的,但不需要所有上下文来提问。 它们都可以是单独的问题,我为每个问题提供了一个链接,表明问题的本质之前已经更简洁地提出过。 如果你隔离你的问题,并将它们从你正在查看的特定代码主体中分离出来,那就更好了——它们可以更容易地被分类为重复项。
常量 INF 有什么用?它不会在程序的任何地方使用。我知道程序员经常在他们的程序中留下一些东西,这些东西目前似乎没有任何用处,但对未来的任何修改都是有用的。INF是否具有相同的目的?如果执行任何类型的修改以使程序更高效或使用其他方法,则使用 INF?
如果删除声明 INF 的行,它是否仍可编译和工作? 会变慢吗? 如果是这样,那是一种神奇的咒语,可以使程序更快,只有在C++秘密社团中才知道。 :-) 如果没有,这只是您怀疑的剩余定义......也许在某个时候使用过,或者也许从未使用过。
看:
如何检测未使用的宏定义和类型定义?
在数组维度中使用左移运算符。例如,int d[1<<20][20]。让移运算符在这个程序中有什么目的?还有其他各种实例在数组维度中使用了 let shift 运算符,我不明白为什么。
在二进制数学中,将 1 向左移动一些位数与将 2 提高到该幂相同。 所以1 << 20
是2^20
,还是1048576。 位移比调用幂函数更快,尽管具有足够优化的幂函数,可以在基数为 2 时的特殊情况......快多少可能不是那么多:
2^n 指数计算真的比位移效率低吗?
小于运算符的重载。在 Point 结构中,小于运算符是重载的。但我似乎找不到它在程序中的哪个位置被调用。它需要一个 Point 对象来调用它,但我找不到任何 Point 对象调用该成员函数的地方。
有人可能会认为,如果你想测试一个方法是否被调用或使用过定义,你可以删除它,看看它是否仍然可以编译。 但在C++,这并不总是奏效;一些定义是重载。 如果删除它们,程序仍会编译,但只是进入更基本的行为。 即使是预处理器宏也可能很有趣,因为一个文件可能会检测到它是否在其他地方定义过,如果没有,则做一些不同的事情......
还有其他方法,例如只是抛出异常或断言它是否在运行过程中被调用。 人们在这里提供了一些其他的想法:
了解是否在C++项目中调用了函数?
正如@BrianSchlenker指出的那样,尽管所示代码中缺少显式调用,但肯定使用了小于运算符。 它用于对map<Point,int> id;
的元素进行排序。 C++ std::map
类型对其内容施加排序,并默认使用 operator<
来实现此排序...尽管您可以覆盖它。 如果您在 less than 函数中打印出某些内容,则每次与 id 映射交互时,您都会看到它被调用。
(注意:如果你想要一个无序列图,你必须使用 std::unordered_map,但这要求你的数据类型具有不同的能力来计算其std::hash
......以及对平等的测试。
通常:此代码不是以可维护或可读的方式进行样式化的。 我建议,如果你想学习提高C++程序性能的方法,你避免阅读你发现的任何一段混淆代码的麻烦......只是因为它碰巧引起了你的注意。
你能从中学习吗? 我想,但去混淆它并评论它将是您的第一步。 这不是一个好主意,特别是如果你必须去要求别人帮助你做到这一点,因为即使他们知道如何......他们可能不想。 更好的方法是通过步骤以稳定的逻辑方式改进您自己的实现,在任何一个步骤中,您都不会超出您的理解范围太远。
(不过,如果你能找到这些东西的原作者,你也许可以让他们参与到关于它的对话中,并为你发表评论。 如果他们没有兴趣,为什么互联网上的随机人会
- 为什么不;名字在地图上是按顺序排列的吗
- 基于多个条件处理地图中的所有元素
- 在C++中将矢量转换为嵌套地图
- 开发C++/地图中的控制台角色扮演游戏
- 如何高效/正确地存储等距游戏的不同类(单个超类的所有子类型)的地图?
- SFML游戏C++中的瓷砖地图碰撞
- 无法访问/存储地图中的游戏状态对象
- 使用什么数据结构来存储基于游戏对象的二维单元地图?
- C++ 文字冒险游戏:2D阵列作为地图
- 在游戏地图上选择100种情况的最佳方法(MFC,CPoint)
- 为 2D 游戏分配图块地图'tiles'和数值
- 编写用于游戏开发的地图编辑器
- 使用C++定向越野地图游戏
- Cocos2d-xTMX地图与游戏精灵共享纹理图像文件
- 基于组件的游戏对象的阵列和地图之间的优点和缺点
- 房间地图游戏程序从文件读取
- 绘制小地图(游戏)
- 游戏开发——制作地图
- 如何在没有平铺地图的情况下使用Cocos2D制作滚动游戏
- (c++,Qt,Linux)将游戏地图线转换为图像线