NOIP 2011 Senior 3

来源:互联网 发布:mac dashboard是什么 编辑:程序博客网 时间:2024/06/06 00:56

Mayan 游戏
总时间限制: 3000ms 内存限制: 65535kB

描述
Mayan puzzle 是最近流行起来的一个游戏。游戏界面是一个 7行 5 列的棋盘,上面堆放着一些方块,方块不能悬空堆放,即方块必须放在最下面一行,或者放在其他方块之上。游戏通关是指在规定的步数内消除所有的方块,消除方块的规则如下:
1、 每步移动可以且仅可以沿横向(即向左或向右)拖动某一方块一格:当拖动这一方块时, 如果拖动后到达的位置 (以下称目标位置) 也有方块, 那么这两个方块将交换位置 (参见输入输出样例说明中的图 6 到图 7) ;如果目标位置上没有方块,那么被拖动的方块将从原来的竖列中抽出,并从目标位置上掉落(直到不悬空,参见下面图 1 和图2) ;

2、 任一时刻,如果在一横行或者竖列上有连续三个或者三个以上相同颜色的方块,则
它们将立即被消除(参见图 1 到图3) 。

注意:
a) 如果同时有多组方块满足消除条件,几组方块会同时被消除(例如下面图 4,三个颜色为 1 的方块和三个颜色为 2 的方块会同时被消除,最后剩下一个颜色为 2 的方块) 。
b) 当出现行和列都满足消除条件且行列共享某个方块时,行和列上满足消除条件的所有方块会被同时消除(例如下面图 5 所示的情形,5 个方块会同时被消除) 。

3、 方块消除之后,消除位置之上的方块将掉落,掉落后可能会引起新的方块消除。注意:掉落的过程中将不会有方块的消除。
上面图 1 到图 3 给出了在棋盘上移动一块方块之后棋盘的变化。 棋盘的左下角方块的坐标为(0, 0) ,将位于(3, 3)的方块向左移动之后,游戏界面从图 1 变成图 2 所示的状态,此时在一竖列上有连续三块颜色为 4 的方块,满足消除条件,消除连续 3块颜色为 4的方块后,上方的颜色为 3 的方块掉落,形成图 3 所示的局面。

输入
第一行为一个正整数 n,表示要求游戏通关的步数。
接下来的 5行,描述 7*5 的游戏界面。每行若干个整数,每两个整数之间用一个空格隔开,每行以一个 0 结束,自下向上表示每竖列方块的颜色编号(颜色不多于 10 种,从 1 开始顺序编号,相同数字表示相同颜色) 。
输入数据保证初始棋盘中没有可以消除的方块。

输出
如果有解决方案,输出 n 行,每行包含 3 个整数x,y,g,表示一次移动,每两个整数之间用一个空格隔开,其中(x,y)表示要移动的方块的坐标,g 表示移动的方向,1 表示向右移动,-1 表示向左移动。注意:多组解时,按照 x 为第一关健字,y 为第二关健字,1优先于-1,给出一组字典序最小的解。游戏界面左下角的坐标为(0,0) 。
如果没有解决方案,输出一行,包含一个整数-1。

样例输入

3 1 0 2 1 0 2 3 4 0 3 1 0 2 4 3 4 0 

样例输出

2 1 1 3 1 1 3 0 1 

提示
按箭头方向的顺序分别为图 6 到图11 .
样例输入的游戏局面如上面第一个图片所示,依次移动的三步是: (2,1)处的方格向右移动, (3,1)处的方格向右移动, (3,0)处的方格向右移动,最后可以将棋盘上所有方块消除。

数据范围
对于 30%的数据,初始棋盘上的方块都在棋盘的最下面一行
对于 100%的数据,0 < n ≤5。

提示:时间限制为3s

不用说,这道题就是道枚举题。有枚举必有优化,由于优化的思路很明了,所以在这里先把可能的优化说完:
1.如果一个方块的左边也有方块,则不要向左移,因为左边的方块已经向右移过了。这个优化是最重要的优化,因为它能将解答树减去很大一截,加上它就足够过了。(时间有3s)
2.若某种颜色的方块存在但又不足3个,则无解。理论上这也是一个强力优化,但实际上只能帮你多过一两个点。
3.不要交换两个同色的方块。

关键是下落和消除的操作。消除稍微简单一点。理解题目后,发现可以这样实现消除:先横向扫描连续块,标记要消除的块,再纵向扫描连续块,标记要消除的块。若有标记,则说明有消除的操作:

bool destruct(int index, int r[X][Y]){    bool sign[X][Y]= {false};    bool bRet = false;    for(int x=0; x<X; x++)    {        int count_ = 1;        int color=r[x][0];        for(int y=1; y<Y; y++)        {            if(r[x][y] != color)            {                if(color && count_ >= 3)                {                    for(int i = y-count_; i<y; i++) sign[x][i]=true;                }                color=r[x][y];                count_ = 1;            }            else if(!r[x][y])            {                count_=0;                color=0;            }            else            {                count_++;            }        }        if(color && count_ >= 3)        {            for(int i = Y-count_; i<Y; i++) sign[x][i]=true;        }    }    for(int y=0; y<Y; y++)    {        int count_ = 1;        int color=r[0][y];        for(int x=1; x<X; x++)        {            if(r[x][y] != color)            {                if(color && count_ >= 3)                {                    for(int i = x-count_; i<x; i++) sign[i][y]=true;                }                color=r[x][y];                count_ = 1;            }            else if(!r[x][y])            {                count_=0;                color=0;            }            else            {                count_++;            }        }        if(color && count_ >= 3)        {            for(int i = X-count_; i<X; i++) sign[i][y]=true;        }    }    for(int x=0; x<X; x++)    {        for(int y=0; y<Y; y++)        {            if(sign[x][y])            {                bRet = true;                count_[index][r[x][y]]--; //某种颜色的个数减一                r[x][y] = 0;            }        }    }    return bRet;}

真正重要的是下落操作。如果在原数据上交换过去交换过来的话,将会很麻烦。如果建立一个临时的数组重新建立一个游戏盘,再复制回原数组中的话就会很简单(就像输入数据一样):

void fall(int r[X][Y]){    int temp[X][Y] = {0};    for(int x=0; x<X; x++)    {        int cy = 0;        for(int y=0; y<Y; y++)        {            if(r[x][y])            {                temp[x][cy++] = r[x][y];            }        }    }    memcpy(r,temp, sizeof(temp));}

最后注意优化一下常数时间。在比赛时能优化常数时间就必须要优化。

参考代码(仅供参考)

#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <string>#include <iostream>#include <algorithm>#include <vector>#include <stack>#include <queue>#include <deque>#include <map>#include <set>using std::cin;using std::cout;using std::endl;inline int readIn(){    int a;    scanf("%d",&a);    return a;}const int X=5;const int Y=7;int n;bool isAns;int rect[7][X][Y];int ins[7][3];int count_[7][15];bool empty(int index){    for(int i=1; i<=10; i++)    {        if(count_[index][i]) return false;    }    return true;}void fall(int r[X][Y]){    int temp[X][Y] = {0};    for(int x=0; x<X; x++)    {        int cy = 0;        for(int y=0; y<Y; y++)        {            if(r[x][y])            {                temp[x][cy++] = r[x][y];            }        }    }    memcpy(r,temp, sizeof(temp));}bool destruct(int index, int r[X][Y]){    bool sign[X][Y]= {false};    bool bRet = false;    for(int x=0; x<X; x++)    {        int count_ = 1;        int color=r[x][0];        for(int y=1; y<Y; y++)        {            if(r[x][y] != color)            {                if(color && count_ >= 3)                {                    for(int i = y-count_; i<y; i++) sign[x][i]=true;                }                color=r[x][y];                count_ = 1;            }            else if(!r[x][y])            {                count_=0;                color=0;            }            else            {                count_++;            }        }        if(color && count_ >= 3)        {            for(int i = Y-count_; i<Y; i++) sign[x][i]=true;        }    }    for(int y=0; y<Y; y++)    {        int count_ = 1;        int color=r[0][y];        for(int x=1; x<X; x++)        {            if(r[x][y] != color)            {                if(color && count_ >= 3)                {                    for(int i = x-count_; i<x; i++) sign[i][y]=true;                }                color=r[x][y];                count_ = 1;            }            else if(!r[x][y])            {                count_=0;                color=0;            }            else            {                count_++;            }        }        if(color && count_ >= 3)        {            for(int i = X-count_; i<X; i++) sign[i][y]=true;        }    }    for(int x=0; x<X; x++)    {        for(int y=0; y<Y; y++)        {            if(sign[x][y])            {                bRet = true;                count_[index][r[x][y]]--;                r[x][y] = 0;            }        }    }    return bRet;}void operate(int index, int x, int y, int g){    memcpy(rect[index+1],rect[index],sizeof(rect[index+1]));    memcpy(count_[index+1],count_[index],sizeof(count_[index]));    index++;#define r rect[index]    std::swap(r[x][y], r[x+g][y]);    fall(r);    bool bOk = true;    while(bOk)    {        bOk = false;        bOk |= destruct(index, r);        if(bOk) fall(r);    }#undef r}void dfs(int index = 0){    if(index>n) return;    if(empty(index))    {        isAns = true;        for(int i=0; i<index; i++)        {            printf("%d %d %d\n",ins[i][0],ins[i][1],ins[i][2]);        }        return;    }    else if(index==n) return;    for(int x = 0; x<X; x++)    {        for(int y = 0; y<Y && rect[index][x][y]; y++)        {            for(int g=1; g>=-1; g-=2)            {                if(x+g<0 || x+g>=X) continue;                if(g==-1 && rect[index][x+g][y]) continue;                if(rect[index][x+g][y] == rect[index][x][y]) continue;                ins[index][0]=x;                ins[index][1]=y;                ins[index][2]=g;                operate(index,x,y,g);                for(int i=1; i<=10; i++)                {                    if(count_[index][i] && count_[index][i]<3) return;                }                dfs(index+1);                if(isAns) return;            }        }    }}void run(){    n=readIn();    for(int x=0; x<X; x++)    {        int y = 0;        while(rect[0][x][y] = readIn())        {            count_[0][rect[0][x][y]]++;            y++;        }    }    dfs();    if(!isAns)        printf("-1\n");}int main(){    run();    return 0;}