算法笔记(VIII) 拼图游戏与深度优先搜索

原题:­

题目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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值