题解:P1312 [NOIP 2011 提高组] Mayan 游戏

· · 题解

[NOIP 2011] Mayan 游戏

这题其实挺简单的吧……。

其实比想象中的好写很多,就是烦,有很多的优化。

思路

这题的话我们可以编写很多的函数来模拟这个过程,例如下落,清理,判空等等,最后呢再利用深搜遍历左移和右移,注意两个方向的优先,一定要小心变量名不要写错,别问为什么,调了一个下午没调出来

函数内容

empty 函数

函数功能:判断区域内是否已经被清空。

bool empty()//判断是否已经清空
{
    for(int i=0;i<5;i++)
        for(int j=0;j<7;j++)
            if(a[i][j])return false;
    return true;
}

copy 函数

函数功能:将数组 x 中的内容赋值到数组 y 上面,一会有用处。

void copy(int x[10][10],int y[10][10])
{
    for(int i=0;i<5;i++)
        for(int j=0;j<7;j++)
            y[i][j]=x[i][j];
}

drop 函数

函数功能:判断区块是否掉落。

void drop()//判断掉落
{
    memset(c,0,sizeof(c));
    for(int i=0;i<5;i++)
        for(int j=0,k=0;j<7;j++)
            if(a[i][j])c[i][k++]=a[i][j];
    copy(c,a);
}

clear 函数

函数功能:按照横向和纵向判断区块数量,可以理解成枚举。

bool clear()
{
    bool flag=false;
    int xx,yy,up,dn;
    for(int i=0;i<3;i++)//横向判断块数不小于 3
        for(int j=0;j<7;j++)
        if(a[i][j])
        {
            for(xx=i;xx<5&&a[xx+1][j]==a[i][j];xx++);//横向
            if(xx-i+1>=3)
            {
                for(int k=i;k<=xx;k++)
                {
                    up=j;dn=j;
                    while(up+1<7&&a[k][up+1]==a[k][j])up++;
                    while(dn-1>=0&&a[k][dn-1]==a[k][j])dn--;
                    if(up-dn+1>=3)
                        for(int l=dn;l<=up;l++)
                            a[k][l]=0;
                    else a[k][j]=0;
                }
                flag=true;
            }
        }
    for(int i=0;i<5;i++)
        for(int j=0;j<5;j++)//纵向判断块数不小于 3
        if(a[i][j])
        {
            for(yy=j;yy+1<7&&a[i][yy+1]==a[i][j];yy++);//纵向
            if(yy-j+1>=3)
            {
                for(int k=j;k<=yy;k++)
                {
                    up=i;dn=i;
                    while(up+1<7&&a[up+1][k]==a[i][k])up++;
                    while(dn-1>=0&&a[dn-1][k]==a[i][k])dn--;
                    if(up-dn+1>=3)
                        for(int l=dn;l<=up;l++)
                            a[l][k]=0;
                    else a[i][k]=0;
                }
                flag=true;
            }
        }
    return flag;
}

深搜就不用说啦,应该有基础的人都知道吧,但是唯一的难点就是回溯,我们需要用几个辅助的容器,把数据复制,这就是前面 copy 函数的作用了。

主函数就更不用说了吧,直接输入然后调用函数就好了。

放一下完整的代码,调了好久啊啊啊啊啊啊啊

代码

#include<cstdio>
#include<algorithm>
using namespace std;
int n,a[10][10],c[10][10],sum[20];
struct node{int x,y,dir;}ans[10];
bool empty()//判断是否已经清空
{
    for(int i=0;i<5;i++)
        for(int j=0;j<7;j++)
            if(a[i][j])return false;
    return true;
}
void copy(int x[10][10],int y[10][10])
{
    for(int i=0;i<5;i++)
        for(int j=0;j<7;j++)
            y[i][j]=x[i][j];
}
void drop()//判断掉落
{
    memset(c,0,sizeof(c));
    for(int i=0;i<5;i++)
        for(int j=0,k=0;j<7;j++)
            if(a[i][j])c[i][k++]=a[i][j];
    copy(c,a);
}
bool clear()
{
    bool flag=false;
    int xx,yy,up,dn;
    for(int i=0;i<3;i++)//横向判断块数不小于3
        for(int j=0;j<7;j++)
        if(a[i][j])
        {
            for(xx=i;xx<5&&a[xx+1][j]==a[i][j];xx++);//横向
            if(xx-i+1>=3)
            {
                for(int k=i;k<=xx;k++)
                {
                    up=j;dn=j;
                    while(up+1<7&&a[k][up+1]==a[k][j])up++;
                    while(dn-1>=0&&a[k][dn-1]==a[k][j])dn--;
                    if(up-dn+1>=3)
                        for(int l=dn;l<=up;l++)
                            a[k][l]=0;
                    else a[k][j]=0;
                }
                flag=true;
            }
        }
    for(int i=0;i<5;i++)
        for(int j=0;j<5;j++)//纵向判断块数不小于3
        if(a[i][j])
        {
            for(yy=j;yy+1<7&&a[i][yy+1]==a[i][j];yy++);//纵向
            if(yy-j+1>=3)
            {
                for(int k=j;k<=yy;k++)
                {
                    up=i;dn=i;
                    while(up+1<7&&a[up+1][k]==a[i][k])up++;
                    while(dn-1>=0&&a[dn-1][k]==a[i][k])dn--;
                    if(up-dn+1>=3)
                        for(int l=dn;l<=up;l++)
                            a[l][k]=0;
                    else a[i][k]=0;
                }
                flag=true;
            }
        }
    return flag;
}
void dfs(int step)
{
    if(step>n)//达到步数
    {
        if(empty())
        {
            for(int i=1;i<=n;i++)
                if(ans[i].dir)printf("%d %d -1\n",ans[i].x+1,ans[i].y);
                else printf("%d %d 1\n",ans[i].x,ans[i].y);
            exit(0);
        }
        return;
    }
    memset(sum,0,sizeof(sum));
    for(int i=0;i<5;i++)
        for(int j=0;j<7;j++)
            sum[a[i][j]]++;
    for(int i=1;i<=10;i++)//若当前状态里同种颜色方块数量不足3,直接返回
        if(sum[i]>0&&sum[i]<3)return;
    for(int i=0;i<4;i++)//右移优先于左移
        for(int j=0;j<7;j++)
            if(a[i][j]!=a[i+1][j])//若颜色相同则没有移动的必要
            {
                int b[10][10];
                copy(a,b);
                ans[step]=(node){i,j,!a[i][j]};//注意判断当前方块是否为空
                swap(a[i][j],a[i+1][j]);
                drop();
                while(clear())drop();
                dfs(step+1);
                copy(b,a);
            }
}
int main()
{
    scanf("%d",&n);
    for(int i=0;i<5;i++)
        for(int j=0;;j++)
        {
            scanf("%d",&a[i][j]);
            if(!a[i][j])break;
        }
    dfs(1);
    printf("-1\n");
    return 0;//好习惯
}

补充一下 Hack 数据

一开始被 Hack 掉了,这个数据主要就是一个关于无效交换的特判,在往右边时不应该多加不同才交换