算法笔记(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­}//func­int 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


0 0
原创粉丝点击