文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>C++实践笔记(三)----找到公主啦,好开心啊!

C++实践笔记(三)----找到公主啦,好开心啊!

时间:2011-04-24  来源:Barryhe

看完电影,突然想到Paris和Helen还在地牢里呆着呢,得在学泛型算法之前救他们出来啊.于是拿起纸和笔坐到马桶上,就开始想了.

回想起那个Helen不动的版本,算法也就是找到了所有的可能性,一步一步的走.那么,既然要找最短路径,我想,找遍所有的可能性是必须的,Helen会动也一样.怎样才能一点都不放过呢?在实践一里面,每走到一点,记录下走到这一点所花的步数,同时做个标记,就不会从别的路来这了.而且,搜寻的时候是广度优先,这样就保证了每到一点所花的步数都是最小的.

但是,Helen如果会动,情况就复杂了.比如可能为了让Helen从某个死胡同里面出来,Paris得重复经过某个点.既然Paris经过某个点可能不只一次,那么,什么是"唯一"的呢??一对点呗!如果某个时刻Paris在A点而Helen在B点,走了n步之后Paris又到了A同时Helen又到了B,那显然这n步是白走了!

好了,那我们搜寻的时候还是广度优先,找这样一对对的点,不就ok了? 

冲水,开始动手写吧!

--------------------------

看题目点这里

--------------------------

 

首先,我们定义FindWay类,学完了C++的容器部分,解决问题果然很有用啊.

先是类的私有成员,我是这样定义的:

typedef pair<int,int> Coordi;
typedef pair<Coordi,Coordi> Dou_coordi;
private:
    vector<vector<char> > maze;//存储地牢
    int lines;//行数
    int columns;//列数
    char pton;//paris向北走时,helen的行动方向
    char ptos;//paris向南走时,helen的行动方向
    char ptow;//paris向西走时,helen的行动方向
    char ptoe;//paris向东走时,helen的行动方向
    Coordi paris;//Paris的位置
    Coordi helen;//Helen的位置
    int steps;//当前已走的步数
    multimap<int,Dou_coordi> came_points;
        //multimap容器,存放paris和helen同时来过的一对对点的集合

根据题目的要求,我们要设定地牢的大小,设置地牢里每一点的标志,放入Paris和Helen,还要设置Helen的行动方向.最后得出需要的步数.因此,我定义了下面几个公共成员函数:

public:
    bool set_size(int m,int n,string &help);//检查地牢的大小是否符合要求,设置地牢大小
    void set_flag(int x,int y,char flag)//设置地牢里每一点的标志
    {
        if(flag=='P')
        {
            paris=make_pair(x,y);
            flag='.';
        }
        else if(flag=='H')
        {
            helen=make_pair(x,y);
            flag='.';
        }
        maze[x][y]=flag;
    }
    bool is_effective(string &help) const;//检查地牢是否是封闭的,以及是否放入了Paris和Helen
    void set_direction(char tn,char ts,char tw,char te)//存入Helen相应的行动方向
    {
        pton=tn;
        ptos=ts;
        ptow=tw;
        ptoe=te;
    }
    int get_steps();//计算并返回步数,如果找不到路则返回-1

set_flag()函数因为要用到很多次,把它定义在头文件中,这样它就是一个内联函数.set_direction()很短,也把他定义在头文件中.另外有些函数中的help参数是为了返回错误信息.

接下来就可以写主函数了:

View Code #include "stdafx.h"
#include <iostream>
#include <string>
#include "FindWay.h"
using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
    FindWay fd;
    cout<<"请输入两个大于等于3小于等于20的数字,分别表示迷宫的行数和列数,用空格隔开:"<<flush;
    int m,n;
    string size_help;
    cin>>m>>n;
    if(!fd.set_size(m,n,size_help))
    {
        cout<<size_help<<endl;
        system("PAUSE");
        return -1;
    }

    cout<<"请输入字符用以描述地图.“.”代表通路,“#”代表岩石,“!”代表熔浆,“H”表示Helen,“P”表示Paris。"<<endl
        <<"输入完第一行再输入第二行,以此类推.用空格隔开."<<endl
        <<"你需要输入"<<m<<"乘以"<<n<<"等于"<<m*n<<"个字符:"<<endl; 
    char flag;
    string maze_help;
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
        {
            cin>>flag;
            fd.set_flag(i,j,flag);
        }
    if(!fd.is_effective(maze_help))
    {
        cout<<maze_help<<endl;
        system("PAUSE");
        return -1;
    }

    cout<<"请输入四个字符“n”(北),“s”(南),“w”(西),“e”(东)的排列,"
        <<"表示Paris分别向NSWE四个方向走时Helen受磁石磁力影响的移动方向:"<<endl;
    char tn,ts,tw,te;
    cin>>tn>>ts>>tw>>te;
    fd.set_direction(tn,ts,tw,te);

    int steps=fd.get_steps();
    if(steps<0)
        cout<<"可惜,找不到可以遇见Helen的道路!"<<endl;
    else
        cout<<"走"<<steps<<"步之后可以遇见Helen!"<<endl;

    system("PAUSE");
    return 0;
}

下面,我们就可以不关心别的,专心实现刚才声明的三个公共成员函数.

其中set_size()和is_effective()这两个函数比较简单:

bool FindWay::set_size(int m,int n,string &help)//检查地牢的大小是否符合要求,设置地牢大小
{
    if(m<3||m>20)
    {
        help="行数应该在3到20之间!";
        return false;
    }
    if(n<3||n>20)
    {
        help="列数应该在3到20之间!";
        return false;
    }
    lines=m;
    columns=n;
    paris=make_pair(0,0);
    helen=make_pair(0,0);
    maze.resize(lines+1,vector<char>(columns+1));
        //为了更易读,不使用下标'0',所以容器要定义的大一号
    return true;
}

 

bool FindWay::is_effective(string &help) const
    //检查地牢是否是封闭的,以及是否放入了Paris和Helen
{
    if(paris.first==0)
    {
        help="没有在地图上指定Paris的位置!";
        return false;
    }
    else if(helen.first==0)
    {
        help="没有在地图上指定Helen的位置!";
        return false;
    }
    for(int i=1;i<=lines;i+=(lines-1))
        for(int j=1;j<=columns;j++)
            if(maze[i][j]=='.'||maze[j][i]=='.')//这样就可以检查地牢的四周
            {
                help="地牢应该是封闭的,四周应该是岩石或者熔浆!";
                return false;
            }
    return true;
}

 

到了最重要的get_steps()函数.在定义它之前,先来定义几个私有成员函数,我们经常要判断Paris或Helen能不能向某个方向移动,如果没有被挡住可以还要判断"这一对点"是不是已经来过了.另外我们要判断Paris和Helen是不是已经相遇了.因此,我定义了下面四个私有成员函数:

private:
    int meet()//判断Paris和Helen是否相遇
    {
        return abs(paris.first-helen.first)+abs(paris.second-helen.second);
        //0表示已经在同一点;1表示相邻,经过一次移动即可相遇
    }
    Coordi &move(char dir,Coordi &new_coordi)//得到Paris或Helen移动后的坐标
    {
        switch (dir)
        {
        case 'n':
            new_coordi.first--;
            break;
        case 's':
            new_coordi.first++;
            break;
        case 'w':
            new_coordi.second--;
            break;
        case 'e':
            new_coordi.second++;
            break;
        }
        return new_coordi;
    }
    bool alive(char p_dir,Coordi &p_coordi,char h_dir,Coordi &h_coordi);
        //Paris和Helen的移动是否会被挡住,可行返回true,并且返回移动后两人的坐标.
    void walk(const char pto,const char hto);
        //向某个方向移动一步,pto表示Paris的移动方向,hto为Helen的移动方向

其中alive()和walk()的定义如下:

typedef multimap<int,Dou_coordi>::iterator Dcoordi_iter;

 

bool FindWay::alive(const char p_dir,Coordi &p_coordi,char h_dir,Coordi &h_coordi)
{
    p_coordi=move(p_dir,p_coordi);
    char p_flag=maze[p_coordi.first][p_coordi.second];
    if(p_flag!='.')
        return false;//Paris不管遇到石头还是熔浆都走不了
    h_coordi=move(h_dir,h_coordi);
    char h_flag=maze[h_coordi.first][h_coordi.second];
    if(h_flag=='!')
        return false;//Helen遇到熔浆就会死
    else if(h_flag=='#')
        h_coordi=helen;//遇到石头就原地不动
    return true;
}

 

void FindWay::walk(const char pto,const char hto)
{
    Coordi new_paris(paris);
    Coordi new_helen(helen);
    Dou_coordi new_points;
    if(alive(pto,new_paris,hto,new_helen))
        //如果不会被挡到,就看这一对点是不是已经来过了
    {
        new_points=make_pair(new_paris,new_helen);
        Dcoordi_iter diter=came_points.begin();
        while(diter!=came_points.end())
        {
            if(diter->second==new_points)
                break;//已经来过了
            diter++;
        }
        if(diter==came_points.end())
            came_points.insert(make_pair(steps+1,new_points));
            //没有来过,就把这一对点插入到came_points容器中
    }
}

 写好这些函数,再写get_steps()就不难了:

int FindWay::get_steps()
{
    steps=0;
    came_points.insert(make_pair(steps,make_pair(paris,helen)));
        //将最开始的一对点插入到容器中
    Dcoordi_iter iter=came_points.begin();
    while(iter!=came_points.end())
    {
        pair<Dcoordi_iter,Dcoordi_iter> diter=came_points.equal_range(steps);
        //广度优先,从所有步数为steps的"点对(Dou_coordi)"分别出发
        while(diter.first!=diter.second)
        {
            paris=(diter.first->second).first;
            helen=(diter.first->second).second;
            int me=meet();
            if(me<=1)
                return steps+me;//遇见!
            walk('n',pton);//向北
            walk('s',ptos);//向南
            walk('w',ptow);//向西
            walk('e',ptoe);//向东
            diter.second=came_points.upper_bound(steps);
                //更新diter.second迭代器,这很重要.
            diter.first++;//步数同为steps的下一点
        }
        iter=diter.second;
        steps++;//步数加1
    }
    return -1;//无法遇见.
}

好吧,事实是,我是先写get_steps()函数,写着写着觉得再写几个私有成员函数就简洁了...

这应该不是最简单的方法,不过感觉类写的还是比较有逻辑性,比较易读的,比Helen不动的那个版本有了进步.

<三傻大闹宝莱坞>是一部难得的佳片,推荐!

附:代码文件(环境是VC++ 2008) 

相关阅读 更多 +
排行榜 更多 +
方块枪战战场安卓版

方块枪战战场安卓版

飞行射击 下载
战斗火力射击安卓版

战斗火力射击安卓版

飞行射击 下载
空中防御战安卓版

空中防御战安卓版

飞行射击 下载