编程之美1.14——连连看工程(含全部代码,伪哈希+BFS)

来源:互联网 发布:叫号软件 编辑:程序博客网 时间:2024/04/28 23:11

1.连连看问题描述:

1.用计算机模型模拟这个问题
2.怎样判断两个图形可以相消(核心)
3.怎样求出相同图形之间的最短路径(转弯次数最少,路径经过的格子最少,后置是在前者的制约下的)动态规划的思想在队列中的实现
4.怎么确定死锁状态(本人的一点小创新,为了优化查找的时间复杂度,开辟了一个伪哈希表)

2.算法思想:

1.如何判断相消:
在本文章中,BFS的思路无疑是最好象,也是最出色,效率最搞得一种方法,网上的一些人说可以之间分类判断贵啊点的数目来判断,那是他们没有仔细思考多种特殊情况就没有是想,光凭想象说的,这里的BFS的思路是最合适的
首先我们以我们第一次选中的节点作为扩展的父节点,然后十字开辟空余的格子,然后分别以这些空余的格子作为父节点再次开辟(当然开辟的次数不能超过三次),还有,这里面我们为了下面好计算最短路径,我们将开始的转弯次数赋值为-1
本读者在这里其实对于搜索最短路径其实有一个自己的想法,我们从完成游戏的角度来看的话,完全没必要非求出最短路径,求出可行路径就可以,但是实际上的连连看游戏电脑是可以输出最短路径的,但是如果采用BFS来做的话,我们只要搜索到就可以从队列中出来,不用继续扩展队列了,除非如果用心的方法的话,那么的确有必要(可能更快)求出满足条件的最短路径

2.如何确定死锁:
在本问题中,我们通过遍历图中所有的剩余的格子,对美个剩余的格子,在图中查找所有的与之图案相同的格子都进行BFS判断一次,成功就返回非死锁
知道所有的空余的格子都被扫描完还是没有发现可行的解,那么就是死锁状态,提前返回失败,游戏结束
本人为了加快查找的效率,利用空间换时间,采用装填因子为200%的拉链法,虽然这已经超过了哈希表可以容忍的范围,但是我们不得不说,这样子的查找效率相对于对图的遍历来说,已经将O(n*n)降到O(C)常熟时间,已经是非常大的优化了,当惹按读者有什么更好的方法,欢迎在评论区联系我,骂我哦

3.扩展问题:

本人目前还在研究如何实现这两个扩展问题,但是思路和想法还是有的
1.优缺点:
我们如果每次都要求出图中任意两点之间的最短路径来求解的话,对于一个高手来说,这要非常的耗时,但是对于一个敌手来说,这要非常的高效
2.不会。。。。。

3.代码实现:

#include"iostream"#include"cstdio"#include"cstring"#include"cstdlib"#define inf 99999999#define N 10//测试用例,40块,每组俩块 //1 3 7 4 -1 -1 5 6 3 2//5 7 8 -1 -1 -1 -1 6 4 7//2 1 -1 -1 -1 -1 -1 -1 0 9//6 -1 -1 -1 -1 -1 -1 -1 -1 8//-1 -1 -1 -1 -1 -1 -1 -1 -1 -1//-1 -1 -1 -1 -1 -1 -1 -1 -1 -1//5 -1 -1 -1 -1 -1 -1 -1 -1 1//6 9 -1 -1 -1 -1 -1 -1 2 4//4 0 0 -1 -1 -1 -1 2 3 8//1 3 0 7 -1 -1 8 9 9 5 using namespace std;typedef struct nod    //伪哈希表的判断成员,每个关键字基本被分配2.5个(涂图有25对的时候),会超过拉链法的120%的要求,但是这已经不是哈希表了,我的优化策略 {int x;int y;struct nod* next;}node;class hash{public:hash(){for(int i=0;i<N;i++) book[i].next=NULL;}~hash()   //有指针的存在,防止内存泄漏 {for(int i=0;i<N;i++){node* p=book[i].next;while(p!=NULL){book[i].next=book[i].next->next;free(p);p=book[i].next;}}}node* returnxy(int);    //按照关键字返回下一个同状态的块的坐标信息void push(node,int);    //int代表关键字,node代表位置,根据这两点在设置图的同时将信息加入哈希表void pop(node,int);    //块已经被删除了,该点弹出哈希表 node*& returnnode(int key){node* p=book[key].next;return p;}private:node book[N];};class point{public:point(){thing=-1;check=death=0;mincross=mindest=inf;   //为了保持最优化转台并且好判断,该两项初始化为无穷大 } void givething(int);   //初始化图的时候和消除的时候的重行分配内容的操作 void choosepoint();    //该点被加入队列,check准备变换,之后的map勒种还要加入全图清楚check标记的函数操作,形参表包含父亲点的坐标,目的是帮助在此函数内修改mincross和mindest成员 void claimdeath();     //该点已经被判断过是思索的状态,这样做是为了方便之后的其他块判断死锁状态时忽略掉改掉,从而加快速度 int returnthing(){return thing;}void initpoint(){check=death=0;mincross=mindest=inf;}void setmincross(int a){mincross=a;}void setmindest(int a){mindest=a;}int returnmincross(){return mincross;}int returnmindest(){return mindest;}int returndeath(){return death;}int returncheck(){return check;}private:bool check;   //当前是否被选中,加入队列中int thing;   //该块中的信息,若为-1,代表此点没有块,为空,否则应该在0-9之间的状态 bool death;   //死锁标记int mincross;   //该点被扩展时的转弯次数,该项要被不断的更新为最小的状态int mindest;   //该点被扩展的时候,在mincross的情况下的距离起点的路程 };class linkup{public:linkup(){memset(queue,0,sizeof(queue));head=tail=0;num=0;startx=starty=endx=endy=0;cross=dest=inf;}void initmap();    //每次消除完或者判断连接失败之后之后,我们都要将图中每个点的queue,head,tail,check,death,mincross,mindest,startx等成员全部清空bool bfs(int,int,int,int);    //后两个形参可以变更为判断死锁的时候的查找点,在游戏进行中的时候,代表的是起点和终点 void creatmap();      //遗留问题,怎么实现随机生成图??? bool spread(int,int,int,int);   //扩展的父亲点,bfs的附属函数 void print();    //打印全图,以便玩家浏览void listenmotion();    //监听玩家的动作,输入坐标bool giveanswer();     //对于玩家的动作,根据bfs进行相应的处理,并且输出语句告知玩家bool judge();    //判断是否死锁,如果死锁我们告知玩家游戏结束 void stream();    //游戏的流程 void clearjudge(){memset(queue,0,sizeof(queue));head=tail=1;}private:point map[N][N];    //10*10的图,50个空位,25对连接hash hashmap;     //哈希表int num;    //目前剩余的块的数目num/2代表目前剩下的对数 int startx;int starty;int endx;int endy;node queue[N*N];int head;int tail;int cross;    //每次连接完之后的最短路径的数据 int dest;};node* hash::returnxy(int key){node* p=book[key].next;if(p==NULL) {cout<<"图存在问题,你可以扇游戏制作者一个巴掌了"<<endl;return NULL;}else{return p;}}void hash::push(node p,int key){node* k=new node;k->x=p.x;k->y=p.y;k->next=book[key].next;book[key].next=k;}void hash::pop(node p,int key)   {node* help=&book[key];node* k=book[key].next;while(k!=NULL){if(k->x==p.x&&k->y==p.y) {help->next=k->next;free(k);break;}else help=k;k=k->next;}}void point::givething(int key){thing=key;}void point::choosepoint(){check=1;}void point::claimdeath(){death=1;}void linkup::creatmap(){cout<<"开始输入你的地图(-1代表空地,0-9代表块)"<<endl;for(int i=0;i<N;i++){for(int j=0;j<N;j++){node p;p.x=i;p.y=j;int key;cin>>key;map[i][j].givething(key);hashmap.push(p,key);if(key!=-1) num++;} }}void linkup::listenmotion(){cout<<"玩家开始输入必要的信息"<<endl;cout<<"起点坐标";cin>>startx>>starty;cout<<"终点坐标";cin>>endx>>endy;cout<<"你的请求已经受理,即将返回你的选择结果"<<endl;}bool linkup::bfs(int sx,int sy,int ex,int ey){tail++;map[sx][sy].setmincross(-1);    //定义刚开始的拐弯数是-1,方便后续计算 map[sx][sy].setmindest(0);if(spread(sx,sy,ex,ey)==1) return true;else{int i=1;while(head!=tail&&i<3)   //控制转弯次数 {if(spread(queue[head].x,queue[head].y,ex,ey)) return true;   //BFS找到了,返回true,在giveanswer中进行删除操作 i++;}    return false;}}bool linkup::giveanswer(){if(map[startx][starty].returnthing()!=map[endx][endy].returnthing()) return false;else{if(bfs(startx,starty,endx,endy))   //可以连接     {    node help;    help.x=startx;help.y=starty;hashmap.pop(help,map[startx][starty].returnthing());help.x=endx;help.y=endy; hashmap.pop(help,map[endx][endy].returnthing());num-=2;    //块消除,数目减少 map[startx][starty].givething(-1);   //消除两个点     map[endx][endy].givething(-1);return true;    }    else return false;     //不能连接 }}void linkup::initmap(){memset(queue,0,sizeof(queue));head=tail=1;for(int i=0;i<N;i++){for(int j=0;j<N;j++) map[i][j].initpoint();}startx=starty=endx=endy=0;cross=dest=inf;}bool linkup::spread(int x,int y,int ex,int ey){queue[head].x=x;queue[head].y=y;int nextstage=tail-1;map[x][y].choosepoint();while(head<=nextstage){x=queue[head].x;   //及时更新新的扩展父节点 y=queue[head].y;    for(int i=y+1;i<N;i++)  //向右     {    if(map[x][i].returncheck()==1&&map[x][i].returnthing()==-1)    {    if(map[x][i].returnmincross()>=map[x][y].returnmincross()+1)    {    map[x][i].setmincross(map[x][y].returnmincross()+1);    map[x][i].setmindest(map[x][y].returnmindest()+i-y);    }    continue;    }    if(map[x][i].returnthing()==-1) <h2>    {</h2>    if(map[x][i].returnmincross()>=map[x][y].returnmincross()+1)    {    map[x][i].setmincross(map[x][y].returnmincross()+1);    map[x][i].setmindest(map[x][y].returnmindest()+i-y);    }    map[x][i].choosepoint();     queue[tail].x=x;    queue[tail].y=i;     tail++;    }    else    {    if(x==ex&&i==ey)    {    cross=map[x][y].returnmincross();    //搜集到最短路径的数据     dest=map[x][y].returnmindest()+i-y;    return true;    }    else break;    }    }    for(int i=y-1;i>=0;i--)    //向左     {    if(map[x][i].returncheck()==1&&map[x][i].returnthing()==-1)    {    if(map[x][i].returnmincross()>=map[x][y].returnmincross()+1)    {    map[x][i].setmincross(map[x][y].returnmincross()+1);    map[x][i].setmindest(map[x][y].returnmindest()+y-i);    }    continue;    }    if(map[x][i].returnthing()==-1)    {    if(map[x][i].returnmincross()>=map[x][y].returnmincross()+1)    {    map[x][i].setmincross(map[x][y].returnmincross()+1);    map[x][i].setmindest(map[x][y].returnmindest()+y-i);    }    map[x][i].choosepoint();    queue[tail].x=x;    queue[tail].y=i;    tail++;    }    else    {    if(x==ex&&i==ey)    {    cross=map[x][y].returnmincross();    dest=map[x][y].returnmindest()+y-i;    return true;    }     else break;    }    }    for(int i=x-1;i>=0;i--)   //向上     {    if(map[i][y].returncheck()==1&&map[i][y].returnthing()==-1)    {    if(map[i][y].returnmincross()>=map[x][y].returnmincross()+1)    {    map[i][y].setmincross(map[x][y].returnmincross()+1);    map[i][y].setmindest(map[x][y].returnmindest()+x-i);    }    continue;    }    if(map[i][y].returnthing()==-1)    {    if(map[i][y].returnmincross()>=map[x][y].returnmincross()+1)    {    map[i][y].setmincross(map[x][y].returnmincross()+1);    map[i][y].setmindest(map[x][y].returnmindest()+x-i);    }    map[i][y].choosepoint();    queue[tail].x=i;     queue[tail].y=y;    tail++;    }    else    {    if(i==ex&&y==ey)    {    cross=map[x][y].returnmincross();    dest=map[x][y].returnmindest()+x-i;    return true;    }    else break;    }    }    for(int i=x+1;i<N;i++)   //向下     {    if(map[i][y].returncheck()==1&&map[i][y].returnthing()==-1)    {    if(map[i][y].returnmincross()>=map[x][y].returnmincross()+1)    {    map[i][y].setmincross(map[x][y].returnmincross()+1);    map[i][y].setmindest(map[x][y].returnmindest()+i-x);    }    continue;    }    if(map[i][y].returnthing()==-1)    {     if(map[i][y].returnmincross()>=map[x][y].returnmincross()+1)    {    map[i][y].setmincross(map[x][y].returnmincross()+1);    map[i][y].setmindest(map[x][y].returnmindest()+i-x);    }    map[i][y].choosepoint();     queue[tail].x=i;    queue[tail].y=y;    tail++;    }     else    {    if(i==ex&&y==ey)     {    cross=map[x][y].returnmincross();    dest=map[x][y].returnmindest()+i-x;    return true;    }    else break;    }    }    head++;}return false;}bool linkup::judge(){for(int i=0;i<N;i++){for(int j=0;j<N;j++){if(map[i][j].returnthing()!=-1&&map[i][j].returndeath()!=1){int key=map[i][j].returnthing();node* p=hashmap.returnnode(key);while(p!=NULL){if(p->x==i&&p->y==j) p=p->next;else if(bfs(i,j,p->x,p->y)) return true;else p=p->next;}clearjudge();}map[i][j].claimdeath();   //判断都不成功,加上死亡标记 }}return false;}void linkup::print(){printf("  ");for(int i=0;i<N;i++) cout<<i<<' ';   //打印表头cout<<endl;for(int i=0;i<N;i++){cout<<i<<' ';    //打印表列 for(int j=0;j<N;j++) {if(map[i][j].returnthing()!=-1) cout<<map[i][j].returnthing()<<' ';else printf("  "); } cout<<endl;} }void linkup::stream(){creatmap();print();while(num!=0){if(judge()==0){clearjudge();cout<<"很不幸,地图死锁,游戏失败!"<<endl;return ;}else{clearjudge();initmap();listenmotion();if(startx<0||startx>=N||starty<0||starty>=N||endx<0||endx>=N||endy<0||endy>=N) {cout<<"输入的坐标点存在错误,请重新输入"<<endl;continue;}if(giveanswer()==1) {cout<<"本次的连接,拐弯次数:"<<cross+1<<"路径长度:"<<dest<<endl;system("PAUSE"); system("cls");print();cout<<"连接有效,开始删除"<<endl;}else{system("cls");print();cout<<"不能连接或者图样不匹配,连接失败!"<<endl;}}}cout<<"全部消除,恭喜你游戏成功!"<<endl;return ;}int main(){linkup my;my.stream();return 0;}

4.结语:

Dijstra,DFS,BFS,A*都可以实现,各有优点,BFS是可以搜索出最优的拐弯数下的最优解路径
BFS可以较快低损耗的求出可行解,不一定最优
A*和Dijstra相对于上面的搜索算法来说,在这种地图下不高效在有些情况的路径下很低效
感谢《编程之美》这本书,我对数的扩展问题真的非常的有兴趣,对于我的代码实现的思路和优化,还有扩展问题的解决,如果读者有更好的思路,欢迎在评论区call我,希望和打架一起分享代码和优化思路的乐趣

1 0
原创粉丝点击