算法笔记(VIII) 拼图游戏与深度优先搜索
来源:互联网 发布:九索数据好不好 编辑:程序博客网 时间:2024/06/10 19:58
原题:
题目2:拼图游戏
Time Limit: 1000ms, Special Time Limit:2500ms, Memory Limit:32768KB
Problem description
你要将一些图形拼成一个正方形,图形的个数从1到5。如下图所示,图形个数是4,
图形不能旋转,拼的时候不能重叠,拼完后的正方形里面不能有空隙。所有给定的图形都要使用。
左面的图表示这样拼不行,右面是一个成功的拼法。
你要判断是否存在一种成功的拼法。如果存在,你要打印出一种拼法。
Input
第一行是一个整数n,表示图形的个数,范围从1到5。
接下来有n个部分,每个部分的第一行是2个整数 i 和 j ,表示下面的 i 行 j 列用来描述一个图形。图形用 0 和 1 表示,1 表示图形占有这个位置,0 表示不占有,中间没有空格。例如上图中图形A的描述是
2 3
111
101
所有图形的长与宽都不超过4。
根据图形给出的顺序给每个图形编号,从1开始,至多到5。
Output
如果不能拼成一个4*4的正方形,就输出“No solution possible”;否则,输出一种拼的方案:一个4*4的正方形的数阵,每个位置上的数字是占有这个位置的图形的编号,中间没有空格。例如上面A、B、C、D的编号依次是1、2、3、4,那么就可以输出 1112 1412 3422 3442 每种方案之间以一个空行相隔
--------------------------------------------------------------------------------------------------------------
思路:
产生四个图块(二维数组),存储相应的块,初始化图形:
为4*4的矩形,初始化为0;
对每一行进行循环,对每一列进行循环;
判别此处的图形有未被填充,
若已经填充,则跳到同一行的下一列继续判别(若以达到最末列,则跳转到下一行);
若未填充,则从未使用的图形块中选择一块;
首先,我们已经知道图形(4*4)上,该列的位置上未有放置,则遍历我们选中的子块;
当然:首先我们需要判别:这个块能否填充这个位置并且未越过界(包括上下界以及左右界)
则(首要条件)
i+block.i-1<=4
即:未越过下边界;
那么: 行约束(下界)已经得到满足;
因此,我们尝试在每一行中循环进行判别;
我们首先在子图块的第一行(即4*4图中的i行)开始遍历:
for(row=i;row<=i+block.i-1;row++)
继而,我们必须寻找到第一行的第一个非零元(这样我们就能判断出子块的位置了,记住这一点)
一旦确定之后,那么剩下的工作的就是:判断每一行有无越界情况,若有,则跳出循环
选取下一个子块尝试填充
若无越界,那么对该行元素尝试填充,一旦发现该填充中非零元与图块中的非零元冲突,则亦不满足条件,跳出
重新选择一个子块填充。
--------------------------------------------------------------------------------
代码:
#include<iostream>using namespace std;#define TRUE 1#define FALSE 0// data definition:int n =4;// int tab[4][2] = {{2,3},{4,2},{2,1},{3,2}};// int dat[22] = {1,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,1,0,1,0,1,1};int tab[4][2] = {{2,3},{4,2},{2,1},{4,2}};int dat[24] = {1,0,1,1,0,1,0,1,0,1,1,1,0,1,1,1,1,0,1,0,1,0,1,1};struct Block{ int row; int col; int **ptr;};struct Order{ int ord; Order* next;};Order* order = new Order;Block blk[4];int Nsolver =0;void init(){//初始化函数; int **tmp ; int offset =0; for(int ib =0;ib<n;ib++){ blk[ib].row =tab[ib][0]; blk[ib].col =tab[ib][1]; tmp = new int* [blk[ib].row]; for(int irow=0;irow<blk[ib].row;irow++){ tmp[irow] = new int[blk[ib].col]; for(int icol=0;icol<blk[ib].col;icol++){ tmp[irow][icol] = dat[offset+(irow)*blk[ib].col+icol]; } } offset +=blk[ib].row*blk[ib].col; blk[ib].ptr = tmp; } tmp =NULL; cout<<endl<<" ---> 初始化图块成功!"<<endl;}void disp(int ib){//验证子块无误函数; cout<<"子块"<<ib<<endl<<blk[ib].row<<" "<<blk[ib].col; for(int irow=0;irow<blk[ib].row;irow++){ cout<<endl; for(int icol=0;icol<blk[ib].col;icol++) cout<<blk[ib].ptr[irow][icol]<<" "; } cout<<endl;}// ----------------- ORDER 处理函数 --------------- //void pop(){// if(order){// if not NULL //delete header; order =order->next; } else cout<<"POP FAILED!"<<endl;}void push(int odr){// Order *tmp = new Order; tmp->ord = odr; tmp->next = order; order = tmp;}int size(Order* head){ int length=0; while(head){ length++; head = head->next; } return length;}int unctn(Order* head ,int ib){ while(head){ if(head->ord==ib){cout<<"子块"<<ib<<"已在!"<<endl; return 0;} head = head->next; } return 1;}void dispOrder(Order* head){ cout<<"ORDER: "; while(head){ cout<<head->ord<<" "; head = head->next; }}//--------------------- 图处理函数 ---------------------//bool fullMap(int** map){ for(int r=0;r<n;r++) for(int c=0;c<n;c++) if (map[c]==0) return FALSE; return TRUE;}int** copyMap(int** map){ cout<<"复制图中...."<<endl; int** locmap; locmap = new int* [n]; for(int i=0;i<n;i++){ locmap[i]=new int[n]; for(int j=0;j<n;j++) locmap[i][j] = map[i][j]; } return locmap;}void dispMap(int** map){ cout<<"-------< MAP >------"<<endl; for(int r=0;r<n;r++){ cout<<endl; for(int c=0;c<n;c++) cout<<map[c]<<" "; } cout<<endl;}bool chkBlk(int** map ,int ib){ //cout<<"将要检验子块"<<ib<<"是否可以填充图..."<<endl; int Row,Col,flag=0; for(int r=0;r<n;r++){ for(int c=0;c<n;c++){ if (map[c]==0){//此处可以填充,在此开始填充; Row = r; Col = c; flag=1; cout<<Row<<"行 "<<Col<<"列可以被填充"<<endl; break; } } if(flag) break;}if(flag==0) cout<<"出错!已经找不到可以被填充的位了!"<<endl;int Ncol;//第一行之非空之列数; if (blk[ib].row+Row <=n){ //下界条件; for(int col=0;col<blk[ib].col;col++){ //确定子块在图中位置; if (blk[ib].ptr[0][col]) {Ncol = col;break;} }// disp(ib); //cout<<"第一行非零元的列数为Ncol:"<<Ncol<<endl; //cout<<"右界条件(要小于4)Col+(blk[ib].col-Ncol)-1:"<<Col+(blk[ib].col-Ncol)-1<<endl; //cout<<"左界条件(要不小于0):Col-Ncol"<<Col-Ncol<<endl; //cout<<(Col+(blk[ib].col-Ncol)-1<4)<<(Col-Ncol>=0)<<endl; if((Col+(blk[ib].col-Ncol)-1<4)&&(Col-Ncol>=0)){//未越过左右界; cout<<"满足左右界条件"<<endl; int icol = Col; //cout<<"可以排入的列号为(图):"<<Col<<endl; //dispMap(map); for(int col=Ncol;col<blk[ib].col;col++){//第一行的判别 if(map[Row][icol]&&blk[ib].ptr[0][col])//冲突的判断; { cout<<"第一行有冲突!"<<endl; return FALSE; } else icol++; }//第一行 for(int row=1;row <blk[ib].row;row++){ icol = Col-Ncol; //cout<<"icol="<<icol<<endl; for(int col=0;col<blk[ib].col;col++) if(map[Row+row][icol]&&blk[ib].ptr[row][col]) { cout<<"图:"<<map[Row+row][icol]<<" 子块"<<blk[ib].ptr[row][col]<<endl; cout<<"第"<<Row+row<<"行有冲突!"<<endl;return FALSE; } else icol++; }//其他行 //------------------------> 开始填充 <--------------------------// cout<<"开始填充...."<<endl; icol = Col; //cout<<"icol="<<icol<<endl; for(int col=Ncol;col <blk[ib].col;col++,icol++)//第一行的填充; if((map[Row][icol]==0)&&(blk[ib].ptr[0][col])){//如果此处为空,并且子块非空; map[Row][icol] = ib+1;//cout<<"icol="<<icol<<endl; } //cout<<"第一行的填充结果:"<<endl; //dispMap(map); for(int row=1;row <blk[ib].row;row++){//其余行的填充; icol = Col-Ncol; for(int col=0;col <blk[ib].col;col++,icol++) if((map[Row+row][icol]==0)&&(blk[ib].ptr[row][col]))//如果此处为空; map[Row+row][icol] =ib+1; //cout<<"第"<<Row+row<<"行的填充结果:"<<endl; //dispMap(map); }//填充完毕; cout<< "子块"<<ib<<"填充完毕!"<<endl<<"当前图为:"<<endl; dispMap(map); return TRUE; }//左右界 else {cout<<"子块"<<ib<<"不满足左右界条件"<<endl ;return FALSE;} }//下界条件else {cout<<"子块"<<ib<<"不满足下界条件"<<endl ;return FALSE;}}// void// ------> 主递归函数;bool chsOrder(int** mapPre){ int** locMap; if (size(order)==n+1){ //已经排完四个子块了; //cout<<"已经排完四个子块了!"<<endl; if (fullMap(mapPre)) { dispMap(mapPre); cout<<"--------------------"<<endl; cout<<"太帅了!你得到第"<<++Nsolver<<"个解"<<endl; getchar();return TRUE;} else return FALSE; }else{ //还没使用完所有子块,故选择下一个满足情况之子块; int ib =0; while(ib<4){ //getchar(); if (unctn(order ,ib)){ //cout<<"子块"<<ib<<"未被使用!"<<endl; locMap = copyMap(mapPre);//接受上次决策后的新图; if (chkBlk(locMap,ib)){//满足填充之条件,满足则就会填充; //cout<<"满足条件,将子块"<<ib<<"弹入栈中!"<<endl; push(ib); //dispOrder(order); chsOrder(locMap);//此子块可以填充,递归选择下一子块; //cout<<"此路不可! 弹出"<<endl; //dispMap(mapPre); //cout<<"-----------"<<endl; //dispMap(locMap); //getchar(); pop(); } } ib++; }// while ib //cout<<"hehe ,怎么也找不到了是吧!"<<endl; }//if not used all}//funcint main(){ cout<<" --------------> 拼图游戏的解法 "; int** map; map = new int*[n]; for(int i=0;i<n;i++){ map[i] = new int[n]; for(int j=0;j<n;j++) map[i][j] =0; } order->ord =n; order->next = NULL; // initialize init(); //disp(); // choose the order chsOrder(map); // cout<<endl<<endl<<"---------> 求解结束!"; getchar();}
后记: 在两年之后的今天,想不到我认识了两位真正的acmer,跟他们的讨论让我觉得这篇日志实在是雕虫小技。拼图游戏其实是dfs方法,当然下面的代码问题在于没有合理的应用状态表示,所以结构显得非常的臃肿。例如拼图完全可以使用二进制位图表示等等,不必显式的使用栈,等等。现在临毕业,时间所限,不再更改了,纯属纪念。
2011-3-24
- 算法笔记(VIII) 拼图游戏与深度优先搜索
- 基于广度优先搜索的拼图游戏算法
- 【算法学习笔记】深度优先搜索(DFS)
- 【算法学习】二、深度优先搜索与广度优先搜索
- 算法:堆栈与深度优先搜索(迷宫问题)
- 算法:堆栈与深度优先搜索(迷宫问题)
- 深度优先搜索算法
- 深度优先搜索算法
- 深度优先搜索算法
- 深度优先搜索算法
- 深度优先搜索算法
- 【深度优先搜索算法】
- 深度优先搜索算法
- 深度优先搜索算法
- 深度优先搜索算法
- 算法---深度优先搜索
- 【算法】深度优先搜索
- 深度优先搜索算法
- System.getenv()和System.getProperty() 的区别
- pycharm教程大全
- 百度地图定位功能的使用
- 文字超出范围用...表示
- Jenkins进阶系列之——05FTP publisher plugin插件
- 算法笔记(VIII) 拼图游戏与深度优先搜索
- C#的多态
- 7.青蛙跳台阶
- MySQL数据库如何解决大数据量存储问题
- Fragment重叠的问题
- Coursera机器学习(Andrew Ng)笔记:异常检测与推荐系统
- POSTMan接口测试之get post
- 多图上传插件
- 修改title旁边的小图标