使用C ++进行定向运动地图游戏

时间:2014-11-29 06:06:38

标签: c++

我使用强力方法解决了一个编程难题,没有动态编程,它工作得很好。这就是谜题:

  

定向运动地图将以下列格式给出。

" ##### "
" #...# "
" #S#G# "
" ##### "
     

通过所有检查点计算从开始到目标的最小距离。

     
      
  • 地图由5个字符组成,如下所示。您可以假设地图不包含任何无效字符,并且地图只有一个起始符号'S'和恰好一个目标符号'G'。
  •   
  • 'S'表示定向越野开始。
  •   
  • 'G'表示定向运动的目标。
  •   
  • '@'表示定向越野检查站。
  •   
  • ''意味着玩家可以通过的开放区块。
  •   
  • '#'表示玩家无法通过的封闭区块。
  •   
  • 只允许垂直或水平(向上,向下,向左或向右)移动一步到下一个块。不允许进行其他类型的移动,例如对角移动(左上,右上,左下和右下)和跳过一个或多个块。
  •   
  • 你不得离开地图。
  •   
  • 距离定义为不同区块的移动次数。
  •   
  • 如果需要,您可以多次传递开放区块,检查点,开始和目标。
  •   
  • 您可以假设参数满足以下条件。
  •   
  • 1< = width< = 100
  •   
  • 1< = height< = 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个具体问题:

  1. 常量INF有什么用?它不会在程序中的任何位置使用。我知道程序员经常在他们的程序中留下一些目前似乎没有任何用处的东西,但是对于将来的任何修改都会有用。 INF是否有同样的目的?如果执行任何类型的修改以提高程序效率或使用其他方法,则使用INF

  2. 在数组维度内使用左移运算符。例如,int d[1<<20][20]。让换班操作员对这个程序有什么目的?有许多其他实例,其中let shift运算符已在数组维度中使用,我无法理解为什么。

  3. 小于运营商的超载。在Point结构中,less-than运算符被重载。但我似乎无法找出它被称为程序的位置。它需要一个Point对象来调用它,但我找不到任何Point对象调用该成员函数的地方。

1 个答案:

答案 0 :(得分:1)

您的问题无效,但不需要所有上下文来询问他们。他们每个人都可以是单独的问题,我为每个人提供了一个链接,表明问题的本质是在更简洁的之前提出的。如果你把问题分开并将它们从你正在看的特定代码体中分离出来,那就更好了 - 它们可以作为重复项更容易地进行分类。

  

常量INF有什么用?它不会在程序的任何地方使用。我知道程序员经常在他们的程序中留下一些目前似乎没有任何用处的东西,但是对于将来的任何修改都会有用。 INF是否有同样的目的?如果执行任何类型的修改以使程序更有效或使用不同的方法,则使用INF?

如果删除声明INF的行,它是否仍然可以编译并工作?变慢了吗?如果是这样,它是一个神奇的咒语,使程序更快,只有在C ++秘密社团中才知道。 :-)如果没有,它只是你怀疑的剩余定义......可能在某个时候使用,或者可能永远不会使用。

请参阅:

How do I detect unused macro definitions & typedefs?

  

在数组维度内使用左移运算符。例如,int d [1&lt;&lt; 20] [20]。让换班操作员对这个程序有什么目的?有许多其他实例,其中let shift运算符已在数组维度中使用,我无法理解为什么。

在二进制数学运算中,向左移位1位数与向该功率提高2位相同。所以1 << 202^20,或1048576.比特转换要比调用幂函数更快,尽管具有足够优化的功率函数,当基数为2时可以是特殊情况...多快多少可能不是那么多:

are 2^n exponent calculations really less efficient than bit-shifts?

  

小于运营商的超载。在Point结构中,less-than运算符被重载。但我似乎无法找出它被称为程序的位置。它需要一个Point对象来调用它,但我找不到任何Point对象调用该成员函数的地方。

有人可能会认为,如果要测试是否曾调用某个方法或使用了某个定义,您可以删除它并查看它是否仍然编译。但是在总是工作的C ++中;一些定义是重载。如果你删除它们,程序仍然会编译,但只是陷入更基本的行为。甚至预处理器宏也可能很有趣,因为一个文件可能会检测到它是否已在其他地方定义,如果不是,则执行不同的操作......

还有其他方法,例如抛出异常或断言是否在运行过程中调用过。人们在这里提出了一些其他想法:

Find out if a function is called within a C++ project?

正如@BrianSchlenker指出的那样,尽管所显示的代码中缺少显式调用,但仍然会使用小于运算符。它用于命令map<Point,int> id;的元素。 C ++ std::map类型对其内容强加排序,默认使用operator<来实现此排序......尽管您可以覆盖它。如果你在小于函数内打印出一些内容,你会在每次与id地图交互时看到它被调用。

(注意:如果你想要一个无序的地图,你必须使用std::unordered_map,但这需要你的数据类型具有不同的能力来计算它的std::hash ......以及测试平等。)


一般而言:此代码不以可维护或可读的方式进行样式化。我建议如果你想学习提高C ++程序性能的方法,你就可以避免读取你发现的任何混淆代码的目标...只是因为它恰好引起了你的注意。

你可以从中学习吗?我想,但是对它进行去混淆并评论它将是你的第一步。不是一个好主意,特别是如果你不得不去请别人帮你做,因为即使他们知道如何......他们可能不想这样做。更好的方法是通过步骤以稳定的逻辑方式改进您自己的实现,在任何一个步骤中,您都不会超出您的理解范围。

(虽然如果你能找到这些东西的原作者,你或许可以让他们参与一个关于它的对话并为你评论。如果他们没有兴趣,为什么会随机的人在互联网上?)