POJ1924 The Treasure

问题描述:

 

这道题目类似于RPG游戏,player的目标是在被monster吃掉之前,拿到treasure。

在游戏中,monster分为aggressive和non-aggressive两种,有不同的吃人能力。

player可以run或walk,时间消耗是一样的。

 

要求:求出最短拿到treasure的时间。

 

 

主要的思路是这样的:使用A*搜索算法,比普通的广度搜索速度上要快一点,但算法上复杂了一些。

                               在程序设计中使用面向对象的思想。

 

源代码如下:

 

Player类:~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

#ifndef PLAYER_H
#define PLAYER_H
#include<iostream>
using namespace std;

class Player{
 
public:
 Player(){
  
  
 }
 
 //overload operator< function for heap comparision
 friend bool operator< (const Player& a, const Player& b){
  
  return a.fCost >=  b.fCost ;
 }
 
 friend ostream& operator<<(ostream& out , const Player& p){
  
  return out<<"("<<p.x<<","<<p.y<<")"<<endl;
 }
 
public:
 
 int x;
 int y;
 int fCost;  //fCost = gCost + Hcost(evaluated)
 int gCost; 
 int second;  //record the time to reach this position
 int myindex;
 int parent;
 const static int direction[8][2]; //move direction
};

#endif

const int Player::direction[8][2]={{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};

 

这里需要注意的是,对于每个类对象共享的数据可以声明为static或const static,但是对于static数据需要在类外初始化

 

 

Monster类:~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

#ifndef MONSTER_H
#define MONSTER_H


class Monster{
 
public :
 Monster(){
   
  pos =NULL;       //初始化指针 
 }
 
 Monster(bool isAggressive,int posNum):aggressiveOrNot(isAggressive),positionNum(posNum){
  
  pos =NULL;
 }
 
 void clean(){
  
  if( pos != NULL){
   delete [] pos;
   pos=NULL;
  }
 }
 
 bool isSafe( int x,int y,int second){
  
  int turn = (second % positionNum)*2 ;
  second ++ ;
  int nextTurn = (second % positionNum)*2 ;
  bool flag1 = false,flag2 = false ;
  
  if( true == aggressiveOrNot ){
  
   flag1 = ( x <= pos[turn]+1 && x>=pos[turn]-1 && y<=pos[turn+1] + 1 && y>=pos[turn +1] -1) ? false : true;
   flag2 = ( x <= pos[nextTurn]+1 && x>=pos[nextTurn]-1 && y<=pos[nextTurn+1] + 1 && y>=pos[nextTurn +1] -1) ? false : true;
  }
  
  else{
   
   flag1 = ( pos[turn] == x && pos[turn+1] == y) ? false: true;
   flag2 = ( pos[nextTurn] == x && pos[nextTurn+1] == y) ? false: true;
  }
  
  return (flag1 && flag2) ;
 }
 
 

public:

 bool aggressiveOrNot;
 int positionNum;
 int* pos;
 static int MosterNum; 
 
};
#endif
int Monster::MosterNum=0;

 

TreasureMap类:~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

 

#ifndef TREASURE_MAP_H
#define TREASURE_MAP_H
#define MAX_MAP_SIZE 10

class TreasureMap{
public:
 TreasureMap(){
  
  
 } 
 
 bool isInBoard(int x,int y){
  
  return ( x<1 || x>height || y<1 || y>width )? false : true;
 }
 
public:

 char board[MAX_MAP_SIZE][MAX_MAP_SIZE];
 int width;
 int height;
 
};

#endif

 

下面是Main函数部分:~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

/*
 * POJ1924 The Treasure
 * 根据player的起始位置,同时需要考虑monster的攻击,在最短的时间内找到treasure的位置
 * 搜索策略:改进的深度优先搜索(A*搜索算法)
*/
#include<iostream>
#include<fstream>
#include<queue>
#include<ctime>
#include<cmath>
#include<stack>
#include "TreasureMap.h"
#include "Player.h"
#include "Monster.h"
#define MAX 100         //预定义Monster的最大数量
#define TABLE_SIZE 1000
using namespace std;

TreasureMap map;
Player player;
Monster* monster;        //Monster数组
int treasureX,treasureY;
priority_queue<Player> PlayerHeap;
bool visitedArray[MAX_MAP_SIZE*MAX_MAP_SIZE+1]; //标记当前的Player位置是否已处于关闭列表中
Player table[TABLE_SIZE];
static int index=0;
Player resultPlayer;

void print(){
 
 for( int i=1 ; i<= map.height ; i++){
  
  for( int j=1 ; j<= map.width ; j++){
   
   cout<<map.board[i][j]<<" ";
  }
  
  cout<<endl;
  
 }
  
 
}

/*
 * 计算gCost的值
 * 评估函数
*/
inline int getHCost(int x,int y){
 
 return abs( x-treasureX )+abs( y- treasureY );
}


void outputPath( Player dest){
 
 
 stack<Player> PlayerStack;
 Player tmp = dest ;
 while( tmp.parent != -1){
  
  PlayerStack.push(tmp);
  tmp = table[ tmp.parent ];
 }
 
 while( ! PlayerStack.empty()){
  
  cout<<PlayerStack.top()<<endl;
  PlayerStack.pop();
  
 }
 
}


void AstarSearch(){
 
 while( ! PlayerHeap.empty())     //每次调用前重新清空堆
  PlayerHeap.pop();
  
 Player tmp,tmp1,curr;         //tmp: walk / tmp1 : run
 bool tmpSafe , tmp1Safe ,tmpTmp ,tmpTmp1 ;    //safe state flag
 int pos,second=0;
 
  
 //step 1: put start state into the heap
 player.fCost=0;
 player.gCost=0;
 player.second=0;
 player.myindex=index++;
 player.parent=-1;
 table[player.myindex] = player;
 PlayerHeap.push(player);
 
 
 
 
 while( ! PlayerHeap.empty()){
  
  
  curr = PlayerHeap.top();
  PlayerHeap.pop();
  pos = (curr.x-1) * map.width + curr.y ;
  
  if( true == visitedArray[pos] ){ //排除重复检测
   
   continue ; 
  }
  
  
  //put state into closed list
  visitedArray[ pos ] =true;   //把检测完的节点放入关闭列表
  map.board[curr.x][curr.y] = 'x'; //标记已经走过(处理过)的位置
  

   
   if( treasureX == curr.x && treasureY == curr.y ){
    
    cout<<"Time used :"<< curr.second << endl;
   resultPlayer = curr ;
    return ;
    
   }
   
   //step 2 :generate new state
   for(int i=0 ; i<8 ;i++){
    
    tmp.x = curr.x + Player::direction[i][0];   //walk
    tmp.y = curr.y + Player::direction[i][1];
    tmp.second = curr.second +1 ;
    
    tmpSafe =  true;         //reset
    tmp1Safe = true ;
    tmpTmp = true ;
    tmpTmp1 = true ;
    
    if( ! map.isInBoard(tmp.x, tmp.y) ){
     
     continue;
    }
    if( '#' == map.board[tmp.x][tmp.y] ){
     
     continue;
    }
    
    tmp1.x = curr.x + Player::direction[i][0]*2;  //run
    tmp1.y = curr.y + Player::direction[i][1]*2;
    tmp1.second= curr.second +1 ;
    
    int j;
    
    //检查walk是否安全
    for(j=0 ; j< Monster::MosterNum ; j++){
     
     
     //如果walk的位置已经被访问过
     //如果该状态已经处于关闭列表
     if( 'x' == map.board[tmp.x][tmp.y] || 
      true == visitedArray[ (tmp.x-1) * map.width + tmp.y ] ){   

      tmpSafe=false;
      break;
     }
      
     if(! monster[j].isSafe( tmp.x , tmp.y , tmp.second  )){
      
      tmpSafe=false;
      
      break;
      
     }
     
     
    }
    
    //检查run是否安全
    
    for(j=0 ; j< Monster::MosterNum ; j++){
     
     //如果run的位置已经被访问过
     //如果该状态已经处于关闭列表
     if( 'x' == map.board[tmp1.x][tmp1.y] ||
      true == visitedArray[ (tmp1.x-1) * map.width + tmp1.y ] ||
      ! map.isInBoard( tmp1.x, tmp1.y ) ||
      '#' == map.board[tmp1.x][tmp1.y]
      ){  

      tmp1Safe=false;
      
      
      break;
     }
     
     if(! monster[j].isSafe( tmp1.x , tmp1.y, tmp1.second )){
       
      tmp1Safe = false;
      
      
      break ;
     }
    }
    
    if( true == tmpSafe ){
     
     tmp.gCost = curr.gCost +1 ;
     tmp.fCost = tmp.gCost + getHCost( tmp.x , tmp.y );
     tmp.myindex = index++ ;
     tmp.parent = curr.myindex ;
     table[tmp.myindex ] = tmp;
     PlayerHeap.push(tmp);
     
    }
    
   
    
    if( true == tmp1Safe ){
     
     
     tmp1.gCost= curr.gCost +1 ;
     tmp1.fCost = tmp1.gCost + getHCost( tmp1.x , tmp1.y );
     tmp1.myindex = index++ ;
     tmp1.parent = curr.myindex ;
     table[ tmp1.myindex ] = tmp1;
     PlayerHeap.push( tmp1 );
     
    }
    

   }
 }
 
 cout<<"impossible"<<endl<<endl;
}

 

int main(){
 

 ifstream in("1924.in");
 char monsterTypeArray[MAX];
 int arrayCount=0,i,j,tmp;
 
 while(1){
  
  in>>map.height>>map.width ;
  if( 0 == map.height && 0 == map.width)
   break;
   
  for(i=1; i<= map.height ; i++){
  
   for(j=1 ; j<= map.width ; j++){
   
    in>>map.board[i][j];
    switch( map.board[i][j] ){
    
     case 'p': player.x = i;
        player.y = j;
        break;
     case 't': treasureX = i;        //switch-case-break!!
        treasureY = j;
        break;
        
     case 'a':
     case 'n': monsterTypeArray[arrayCount++]=map.board[i][j];
        break;
        
     default: break;
    }
   }
  }
  
  in>>Monster::MosterNum;    //Monster的数量
  if( 0 != Monster::MosterNum ){
   
   monster=new Monster[Monster::MosterNum];
    for(i=0 ; i<arrayCount ; i++){
   
     in>>monster[i].positionNum;
   
   
     monster[i].aggressiveOrNot = ( 'a' == monsterTypeArray[i] ) ? true : false;
     monster[i].pos=new int[ monster[i].positionNum *2 ];
     for(j=0 ;j< monster[i].positionNum *2 ; j++)
      in>>monster[i].pos[j];
    
   
    }
  }
  
  
   //in>>tmp;         //cin、in直接跳过空行和空格
   clock_t time=clock();
         AstarSearch();
      cout<<"计算用时:"<<clock()-time<<"MS"<<endl;
         outputPath(resultPlayer);
  
 }
 
 
 return 0;
}

 

这里存在的可改进的地方:

1、由于题目需要进行大量的输入,所以避免使用C++中的流操作cin,而是使用C中的scanf函数,这样可以节省很多时间。

2、由于题目中run和walk的两种情况存在一定的逻辑关系,所以在排除不合理情况中可以进一步提前判断排除。

3、题目中可能存在player需要stand still的情况,但是我这一点没有考虑好,感觉太复杂了。

可以参考下面的这篇文章:http://www.chhaya.me/?p=204

 

这里我总结一下对A*算法的思想的体会:

 

A*算法的主要步骤是这样的:

1、首先将开始节点放入二叉堆中。

2、将二叉堆中的首元素弹出,放入关闭列表,判断它是否为目标节点,如果是,搜索算法结束,输出相关结果信息。如果不是,跳到第3步。

3、产生当前节点的所有可能的子节点。判断这些子节点是否可以放入堆中(如果已经处于关闭列表中,忽略 ;如果不处于关闭列表中,就放入堆中,这里由于数据结构使用的STL中的优先队列,所以没有考虑判断是否可以更新堆中已有的子节点,将所有符合筛选条件的节点都放入堆中),然后根据评估函数计算出这些子节点的fCost(和父节点有关的,fCost = gCost + Hcost)。

4、不断循环直到找到目标节点。

 

这里需要注意的地方

1、由于没有对已经在堆中的节点进行判和更新,所以在堆中可能存在重复的节点,因此一旦这个节点弹出了,进入关闭列表了,那么后面的所有这个节点的副本都应该直接忽略掉。另外如果是类似于地图的搜索,那么对于已经进入关闭列表的元素,就需要做一下已访问过的标记,防止“死循环”访问。

 

2、对于一些复杂的应用,可能在向堆中插入子节点前,要进行重复状态检测,而这个就需要使用Hash来完成。

 

3、有时候为了还原搜索目标节点的路径,需要在节点中记录myindex和parent信息,而这时就需要利用数组来存储各个状态节点了,因为节点一旦被检测完毕,就会被从堆中弹出。

 

 

你可能感兴趣的:(数据结构,游戏,算法,table,null,delete)