可以忽略的日常废话

emmmm怎么说得写了两天多叭(时间加吧加吧差不多48h),不过好歹终于写出来了
变身爆OJ小能手.jpg(luogu有一页的窝emm)

开始正文


琪露诺的自闭小屋
题目都长到用图片展示了,那么这个题到底想让我们模拟些什么呢?

开始前的特别提醒

1.输出的内容不要拼错,不要漏下最后的标点符号
2.一定要拿草稿纸记下来每个操作的坑点,在写代码之前想想如果自己是出题人可能会怎么卡这个操作(努力想各种迷惑行为)
我们分子任务看

子任务1

只有冰雪弹幕和造冰砖

实现到是不难,有些小坑要注意
1.其实高空有冰砖时冷冻值并不需要加5,因为加5真的没有卵用(而且到后面有砖摔碎了还比较麻烦),老老实实维护地面的就好。如果地面上有冰砖,冷冻值要先设置为inf。
2.注意判断边界
在linux下数组越界可是会有一些神奇的wa
3.注意收集完之后冷冻值清零
4.如果没有make_roof的操作就不要判断perfect了
20pts轻松到手
核心代码如下:

void ice_barrage(int r,int c,int d,int s)
{
    int dong=0;
    for(int i=0;i<=s;i++)
    {
        int gx,gy;
        if(!d) gx=r-i,gy=c;
        if(d==1)gx=r-i,gy=c-i;
        if(d==2)gx=r,gy=c-i;
        if(d==3)gx=r+i,gy=c-i;
        if(d==4)gx=r+i,gy=c;
        if(d==5)gx=r+i,gy=c+i;
        if(d==6)gx=r,gy=c+i;
        if(d==7)gx=r-i,gy=c+i;
        if(gx<0||gy<0)break;
        if(gx>=n||gy>=n)break;
        if(ld[gx][gy]==20)break;//这里我采用冷冻值为20代表砖
        if(ld[gx][gy]<4)ld[gx][gy]++,dong++;
    }
    printf("CIRNO FREEZED %d BLOCK(S)\n",dong);
}
void make_ice_block()
{
    int co=0;
    for(int i=0;i<n;i++)
     for(int j=0;j<n;j++)
      {
        if(ld[i][j]==4)bz++,co++,ld[i][j]=0;//收集砖之后记得冷冻值清零
      }
    printf("CIRNO MADE %d ICE BLOCK(S),NOW SHE HAS %d ICE BLOCK(S)\n",co,bz);
}
子任务2

没有remove操作以及之后的玩意
那就只有put_ice_block呗
可以用0,1表示(i,j,k)是否有砖。
一个不在地面的砖可以放置的前提是6个方向(上,下,左,右,前,后)有砖。没错上面有砖也可以。在判旁边的砖的时候一定要判是否越界如果放在地面上,记得把冷冻值改为inf。最后记得把题目中给出的在房间内/外的条件抄上(再次提醒不要抄错)。
30pts到手

bool sl(int r,int c,int h)
{
    if(r-1>=0&&mp[r-1][c][h])return false;
    if(r+1<n&&mp[r+1][c][h])return false;
    if(c-1>=0&&mp[r][c-1][h])return false;
    if(c+1<n&&mp[r][c+1][h])return false;
    if(h-1>=0&&mp[r][c][h-1])return false;
    if(h+1<n&&mp[r][c][h+1])return false;
    return true;
}
void put_ice_block(int r,int c,int h)
{
    if(!bz)
    {
        printf("CIRNO HAS NO ICE_BLOCK\n");
        return;
    }
    if(mp[r][c][h]||(h>0&&sl(r,c,h)))//如果这个砖它死了
    {
        printf("BAKA CIRNO,CAN'T PUT HERE\n");
        return ;
    }
    mp[r][c][h]=1;bz--;
    if(!h) ld[r][c]=20;
    if(r<hr||r>hr+hx-1||c<hc||c>hc+hy-1)//在屋外
          printf("CIRNO MISSED THE PLACE\n");
        else if(hr+1<=r&&r<=hr+hx-2&&hc+1<=c&&c<=hc+hy-2)//在屋内
      printf("CIRNO PUT AN ICE_BLOCK INSIDE THE HOUSE\n");
    else printf("CIRNO SUCCESSFULLY PUT AN ICE_BLOCK,NOW SHE HAS %d ICE_BLOCK(S)\n",bz);
}
子任务3

没有make_roof操作,自然也就建不成小屋,不用判perfect
新增操作remove_ice_block
如果是在地面,冷冻值清零。如果在高空,向六个方向用bfs高出悬空的联通块(即这个连通块里没有任何一个点的高度是0),并且将所有入过队的点标记为0(没有砖)。
bfs的要点依旧是判边界
50pts到手

struct dl{
    int x,y,z;
    dl(int xx,int yy,int zz):x(xx),y(yy),z(zz){}//一个构造函数
};
queue <dl> q,as;
int dx[6]={-1,1,0,0,0,0},dy[6]={0,0,-1,1,0,0},dz[6]={0,0,0,0,-1,1};
bool vis[N][N][N];
bool jr(int x,int y,int z)//在bfs中判断合法
{
    if(vis[x][y][z]||(!mp[x][y][z]))return false;
    if(x>=n||y>=n||z>=hm)return false;
    if(x<0||y<0||z<0)return false;
    return true;
}
int bfs(int r,int c,int h)
{
    if(vis[r][c][h]||r<0||r>=n||c<0||c>=n||h<0||h>=hm)return 0;
    if(!mp[r][c][h])return 0;//注意一下初始点是否有砖
    while(!q.empty())q.pop();
    while(!as.empty())as.pop();//as记录如果队的所有点,方便处理是悬空联通块的情况
    q.push(dl(r,c,h));as.push(dl(r,c,h));//貌似as可以用数组实现的说
    bool bj=0;//记录是否有地面
    if(h==0)bj=1;
    vis[r][c][h]=1;
    while(!q.empty())
    {
        dl ex=q.front();
        q.pop();
        for(int i=0;i<6;i++)
        {
            int xx=ex.x+dx[i],yy=ex.y+dy[i],zz=ex.z+dz[i];
            if(jr(xx,yy,zz))
            {
                if(!zz)bj=1;//处理出当前连通块中所有能够到达的点,所以不直接break
                q.push(dl(xx,yy,zz));vis[xx][yy][zz]=1;
                if(!bj)
                 as.push(dl(xx,yy,zz));
            }
        }
    }
    if(bj) return 0;
    int cnt=0;
    while(!as.empty())
    {
        dl ex=as.front();as.pop();
        cnt++;mp[ex.x][ex.y][ex.z]=0;
    }
    return cnt;
}
void remove_ice_block(int r,int c,int h)
{
    if(!mp[r][c][h])
    {
        printf("BAKA CIRNO,THERE IS NO ICE_BLOCK\n");
        return ;
    }
    mp[r][c][h]=0;bz++;
    if(!h)ld[r][c]=0;//注意维护地面
    memset(vis,0,sizeof(vis));
    int ss=bfs(r+1,c,h)+bfs(r-1,c,h)+bfs(r,c+1,h)+bfs(r,c-1,h)+bfs(r,c,h+1)+bfs(r,c,h-1);//注意6个方向都要来一遍
        if(ss)
     printf("CIRNO REMOVED AN ICE_BLOCK,AND %d BLOCK(S) ARE BROKEN\n",ss);
    else printf("CIRNO REMOVED AN ICE_BLOCK\n");
}
子任务 4

琪露诺在移动冰砖的时候不会有方块掉落
emmmm好像在上一个子任务的时候解决掉了诶。(完成这个子任务不需要bfs)
还是讲讲make_roof叭
1.找出墙壁的最大高度,对所有的墙壁for一遍取max即可(啥你问我为什么不在put_ice的时候维护?因为我不想在摔掉冰砖的时候维护)
2.判断空间。由题意可得在hx或者hy<3的情况下,即使最大高度够2,也会导致空间不足,所以不需要计算,直接判断即可。
3.对屋顶所在高度的区域for一遍统计需要的冰砖个数。如果库存冰砖不够,直接进入特殊情况,因为此时没有屋顶,所以不用评价小屋
4.回收冰砖。屋内屋外分别统计,由于琪露诺不会在这个过程中产生摔碎的冰砖,所以直接库存++即可。
5.考虑屋顶塌陷
类似这种图的数据屋顶会死掉

后面的砖懒得画(bushi(请原谅博主有限的画图能力和空间想象能力qwq)
所以在移出多余的砖之后要再bfs一遍(和上面remove的bfs是一样的)。如果bfs返回值非0,那么关于屋顶,它死了,并且因为没有屋顶,所以不需要评价小屋
注意即使屋顶塌陷了,那么也要输出房屋内/外收集的冰砖
貌似也很好实现

bool t1,t2,t3;//记录进入哪个特殊情况
bool wai(int x,int y,int z)//判断砖屋外
{
    if((x<hr)||x>hr+hx-1)return true;
    if((y<hc)||(y>hc+hy-1))return true;
    if(z>mxh+1)return true;
    return false;
}
bool nei(int x,int y,int z)//判断砖在屋内
{
    if((x>hr)&&(x<hr+hx-1)&&(y>hc)&&(y<hc+hy-1)&&(z<=mxh))return true;
    return false;
}
bool yy,nie,roof;//屋外是否有回收,屋内是否有回收,是否有建房顶的工作(判断perfect要用)
void make_roof()
{
        for(int i=hc;i<hc+hy;i++)//统计最大高度
         for(int k=hm;k>=0;k--)
         {
             if(mp[hr][i][k]){mxh=max(mxh,k);break;}
             if(mp[hr+hx-1][i][k]) {mxh=max(k,mxh);break;}
         }
    for(int i=hr+1;i<hr+hx-1;i++)
     for(int k=hm;k>=0;k--)
     {
        if(mp[i][hc][k]){mxh=max(mxh,k);break;}
        if(mp[i][hc+hy-1][k]){mxh=max(mxh,k);break;}
     }
    int cnt=0;roof=1;
    for(int i=hr;i<hr+hx;i++)//统计造房顶需要的冰砖
     for(int j=hc;j<hc+hy;j++)
      if(!mp[i][j][mxh+1])cnt++,mp[i][j][mxh+1]=1;
    if(bz<cnt)
    {
        t1=1;printf("SORRY CIRNO,NOT ENOUGH ICE_BLOCK(S) TO MAKE ROOF\n");return ;
    }
    if(mxh<1||hx<3||hy<3)//判断空间
    {
        t2=1;printf("SORRY CIRNO,HOUSE IS TOO SMALL\n");return ;
    }
    bz-=cnt;
    int ne=0,wa=0;
    for(int i=0;i<n;i++)//统计屋内,屋外的多余冰砖
      for(int j=0;j<n;j++)
        for(int k=0;k<=hm;k++)
          {
            if(mp[i][j][k]&&wai(i,j,k)) yy=1,bz++,mp[i][j][k]=0,wa++;
            if(mp[i][j][k]&&nei(i,j,k)) nie=1,bz++,mp[i][j][k]=0,ne++;
          }
    memset(vis,0,sizeof(vis));
    int mbl=bfs(hr,hc,mxh+1);//判断房顶是否塌陷
    printf("%d ICE_BLOCK(S) INSIDE THE HOUSE NEED TO BE REMOVED\n",ne);
    printf("%d ICE_BLOCK(S) OUTSIDE THE HOUSE NEED TO BE REMOVED\n",wa);
    if(mbl)
    {
        t3=1;printf("SORRY CIRNO,HOUSE IS BROKEN WHEN REMOVING BLOCKS\n");return ;
    }
}
子任务 5

门不会在柱子旁边。所以即使柱子旁边有残缺,也不会作为门的候选方案。对于这种特殊情况,可以按照是否构成高度为2的门为第一关键字,是否更靠近中间为第二关键字选门。对于所有的候选方案排序即可。
统计残缺:对四个墙for一遍,把高度大于1(因为地面的高度是0)的残缺统计起来即可
统计柱子:在统计墙的时候如果是在四角,就加入柱子中。
统计门的候选方案:记录不在四角且高度<2的残缺,不需要特判是否在柱子旁边,因为排序的关键字会把在柱子旁边的候选方案排到后面去
在确定门之后,把其余的候选方案造成的残缺统计到总残缺里面。如果不能构成高度为2的门,则要回收一个冰砖来开门,所以库存++。(竟然不是直接碎掉可真是神奇)
因为门不在柱子旁边所以也不需要考虑什么柱子的残缺转换为墙的残缺之类的玩意。
注意如果琪露诺的库存冰砖不够修补柱子,那么最后她的库存数是0。
最后判断是否perfect即可。
这个策略在子任务4中也是可以过的,所以90pts get
只有90的代码

int clo(int x,int y)//评价一个候选方案与中间位置的接近度(为0表示在中间位置)
{
    int midx,midy,mx1=100000,my1=100000;
        if(hx%2) midx=hr+hx/2;
        if(hy%2) midy=hc+hy/2;
        if(!(hx%2))midx=hr+hx/2-1,mx1=midx+1;
        if(!(hy%2))midy=hc+hy/2-1,my1=midy+1;
//midx,midy分别表示hx(hy)为奇数时的中间位置,为偶数时x(y)较小的位置,mx1(my1)表示hx(hy)为偶数时x(y)较大的中间位置
        int rx=min(abs(midx-x),abs(mx1-x));
    int ry=min(abs(midy-y),abs(my1-y));
    return min(rx,ry);
}
bool cmp(ddl a,ddl b)//因为写着写着ce了,所以又定义了一个结构体,里面的东西和dl是一样的
{
    int kva=1,kvb=1;if(a.z)kva=0;if(b.z)kvb=0;
    if(!mp[a.x][a.y][kva]&&mp[b.x][b.y][kvb])return 1;//按照门的高度排序
        if(mp[a.x][a.y][kva]&&!mp[b.x][b.y][kvb])return 0;
        return clo(a.x,a.y)<clo(b.x,b.y);//按照更靠近中间排序
}
void perfect()
{
    if(t1||t2||t3||!roof)return ;
    int hm=0,cq=0,zz=0;
    for(int i=hc;i<hc+hy;i++)//统计平行于x轴的两面墙(码风丑陋.jpg)
      for(int k=0;k<=mxh;k++)
      {
        if(!mp[hr][i][k])
        {
            if(k>1&&i!=hc&&i!=hc+hy-1)cq++;//统计残缺
            else if(i==hc||i==hc+hy-1) zz++;//统计柱子
            else men[++hm].x=hr,men[hm].y=i,men[hm].z=k;//统计门的候选方案
        }
        if(!mp[hr+hx-1][i][k])
        {
            if(k>1&&i!=hc&&i!=hc+hy-1)cq++;
                    else if(i==hc||i==hc+hy-1) zz++;
                    else men[++hm].x=hr+hx-1,men[hm].y=i,men[hm].z=k;
        }
      }
    for(int i=hr+1;i<hr+hx-1;i++)//统计与y轴平行的两面墙
      for(int k=0;k<=mxh;k++)
      {
        if(!mp[i][hc][k])
        {
            if(k>1&&i!=hr&&i!=hr+hx-1)cq++;
                    else men[++hm].x=i,men[hm].y=hc,men[hm].z=k;
        }
        if(!mp[i][hc+hy-1][k])
        {
            if(k>1&&i!=hr&&i!=hr+hx-1)cq++;
                    else men[++hm].x=i,men[hm].y=hc+hy-1,men[hm].z=k;
        }
      }
    int emm=0; ddl mmen;
    if(hm)//选门+处理不是最后的门的残缺
    {
            sort(men+1,men+1+hm,cmp);
            mmen.x=men[1].x;mmen.y=men[1].y;mmen.z=men[1].z;
        if(!mp[mmen.x][mmen.y][mmen.z^1])emm=1;//判断门的高度
                if(!emm)bz++;//如果高度不够2,回收冰砖(虽然咱也不知道为什么没有这句也可以有90分)
        for(int i=1;i<=hm;i++)
        {
            if(men[i].x==mmen.x&&men[i].y==mmen.y)continue;
            cq++;
        }
    }
       if(bz<cq)
       {
        printf("SORRY CIRNO,NOT ENOUGH ICE_BLOCKS TO FIX THE WALL\n");
        return ;
    }
    printf("GOOD JOB CIRNO,SUCCESSFULLY BUILT THE HOUSE\n");
    if(!emm)
     printf("HOUSE HAS NO DOOR\n");
    else printf("DOOR IS OK\n");
    if(cq) printf("WALL NEED TO BE FIXED\n");
    else printf("WALL IS OK\n");
    if(zz) printf("CORNER NEED TO BE FIXED\n");
    else printf("CORNER IS OK\n");
    bz-=cq;bz-=zz;bz=max(bz,0);
    printf("CIRNO FINALLY HAS %d ICE_BLOCK(S)\n",bz);
    if(!yy&&!nie&&!cq&&emm&&!zz&&clo(mmen.x,mmen.y)==0)printf("CIRNO IS PERFECT!\n");
}
子任务 6

我们现在要考虑门和柱子的关系。如果门开在柱子旁边,那么柱子上的残缺有可能被看见
考虑门开在柱子旁边与柱子上的残缺的情况(以下讨论柱子的残缺其高度h<2)
1.如果门是天然形成的高度为2,那么一定会看到柱子上的残缺(即一定转化)
2.选定作为门的位置高度为1
如果柱子上的残缺和选择作为门的地方的残缺高度不同,则视线会被挡住,从而不转化为壁的残缺(无论柱子上的残缺是否连续),即与门的“残缺”高度相同的柱子上的残缺转化为墙壁上的残缺
还是画个图康康叭(灵魂画师上线)

这是高度不同的情况,发现最下面的角从屋内看不到,所以不视为墙壁残缺(作为门的地方无论是否形成高度为2的门,都不视为墙壁残缺)

那么高度为1(地面高度为0)的那一块柱子上的残缺可以被看到,所以转化为墙壁的残缺
代码如下:

    if(mmen.x==hr+1||mmen.x==hr+hx-2)
        {
         int ying;
         if(mmen.x==hr+1)ying=hr;
         if(mmen.x==hr+hx-2) ying=hr+hx-1;
         for(int k=0;k<=1;k++)
          if(!mp[ying][mmen.y][k]&&!mp[mmen.x][mmen.y][k])cq++,zz--;
         }
     else if(mmen.y==hc+1||mmen.y==hc+hy-2)
        {
         int ying;
         if(mmen.y==hc+1)ying=hc;
         if(mmen.y==hc+hy-2)ying=hc+hy-1;
         for(int k=0;k<=1;k++)
             if(!mp[mmen.x][ying][k]&&!mp[mmen.x][mmen.y][k])cq++,zz--;
       }

于是我们交了一下,发现


wa哪了nie?

难道选天然的高度为2的残缺作为门不是更省冰砖吗?
当然不是,准确的说,当没有高度为2的门,且有备选方案在墙角的时候,就要考虑墙角的损耗
考虑一个在柱子旁边的备选方案对减少残缺的贡献。如果要在这个备选方案处开门,那么最终墙角的所有残缺都会被看到。
我们以选择这个门,对最终残缺(即如果把所有备选方案都视作墙壁残缺后的残缺值)减少的值为这个备选方案的价值(越大越好)。
计算价值:

int val(ddl a)
{
    int gao=0;
    if(!mp[a.x][a.y][0])gao++;
    if(!mp[a.x][a.y][1])gao++;
    if((a.x==hr+1&&a.y==hc)||(a.x==hr&&a.y==hc+1))
    {
        if(!mp[hr][hc][0])gao--;
        if(!mp[hr][hc][0])gao--;
    }
    if((a.x==hr&&a.y==hc+hy-2)||(a.x==hr+1&&a.y==hc+hy-1))
    {
        if(!mp[hr][hc+hy-1][0])gao--;
        if(!mp[hr][hc+hy-1][1])gao--;
    }
    if((a.x==hr+hx-2&&a.y==hc+hy-1)||(a.x==hr+hx-1&&a.y==hc+hy-2))
    {
        if(!mp[hr+hx-1][hc+hy-1][0])gao--;
        if(!mp[hr+hx-1][hc+hy-1][1])gao--;
    }
    if((a.x==hr+hx-2&&a.y==hc)||(a.x==hr+hx-1&&a.y==hc+1))
    {
        if(!mp[hr+hx-1][hc][0])gao--;
        if(!mp[hr+hx-1][hc][1])gao--;
    }
    return gao;
}

接下来,我们以天然形成的门的高度为第一关键字,价值为第二关键字,是否靠近中间为第三关键字排序,进行选门。
完整的检查小屋

int clo(int x,int y)
{
    int midx,midy,mx1=100000,my1=100000;
        if(hx%2) midx=hr+hx/2;//这里的注释大概被我吃了叭
        if(hy%2) midy=hc+hy/2;
        if(!(hx%2))midx=hr+hx/2-1,mx1=midx+1;
        if(!(hy%2))midy=hc+hy/2-1,my1=midy+1;
        int rx=min(abs(midx-x),abs(mx1-x));
    int ry=min(abs(midy-y),abs(my1-y));
    return min(rx,ry);
}
int val(ddl a)
{
    int gao=0;
    if(!mp[a.x][a.y][0])gao++;
    if(!mp[a.x][a.y][1])gao++;
    if((a.x==hr+1&&a.y==hc)||(a.x==hr&&a.y==hc+1))
    {
        if(!mp[hr][hc][0])gao--;
        if(!mp[hr][hc][0])gao--;
    }
    if((a.x==hr&&a.y==hc+hy-2)||(a.x==hr+1&&a.y==hc+hy-1))
    {
        if(!mp[hr][hc+hy-1][0])gao--;
        if(!mp[hr][hc+hy-1][1])gao--;
    }
    if((a.x==hr+hx-2&&a.y==hc+hy-1)||(a.x==hr+hx-1&&a.y==hc+hy-2))
    {
        if(!mp[hr+hx-1][hc+hy-1][0])gao--;
        if(!mp[hr+hx-1][hc+hy-1][1])gao--;
    }
    if((a.x==hr+hx-2&&a.y==hc)||(a.x==hr+hx-1&&a.y==hc+1))
    {
        if(!mp[hr+hx-1][hc][0])gao--;
        if(!mp[hr+hx-1][hc][1])gao--;
    }
    return gao;
}
bool cmp(ddl a,ddl b)
{
    int kva=1,kvb=1;if(a.z)kva=0;if(b.z)kvb=0;
    if(!mp[a.x][a.y][kva]&&mp[b.x][b.y][kvb])return 1;
        if(mp[a.x][a.y][kva]&&!mp[b.x][b.y][kvb])return 0;
        int va=val(a),vb=val(b);
    if(va!=vb) return va>vb;
    return clo(a.x,a.y)<clo(b.x,b.y);
}
void perfect()
{
    if(t1||t2||t3||!roof)return ;
    int hm=0,cq=0,zz=0;
        for(int i=hc;i<hc+hy;i++)//统计墙,残缺,门的备选方案
          for(int k=0;k<=mxh;k++)
          {
            if(!mp[hr][i][k])
            {
            if(k>1&&i!=hc&&i!=hc+hy-1)cq++;
            else if(i==hc||i==hc+hy-1) zz++;
            else men[++hm].x=hr,men[hm].y=i,men[hm].z=k;
         }
         if(!mp[hr+hx-1][i][k])
        {
        if(k>1&&i!=hc&&i!=hc+hy-1)cq++;
            else if(i==hc||i==hc+hy-1) zz++;
            else men[++hm].x=hr+hx-1,men[hm].y=i,men[hm].z=k;
         }
       }
     for(int i=hr+1;i<hr+hx-1;i++)
      for(int k=0;k<=mxh;k++)
      {
        if(!mp[i][hc][k])
        {
        if(k>1&&i!=hr&&i!=hr+hx-1)cq++;
            else men[++hm].x=i,men[hm].y=hc,men[hm].z=k;
        }
        if(!mp[i][hc+hy-1][k])
        {
        if(k>1&&i!=hr&&i!=hr+hx-1)cq++;
            else men[++hm].x=i,men[hm].y=hc+hy-1,men[hm].z=k;
        }
      }
    int emm=0; ddl mmen;
    if(hm)//如果能找到备选方案,则进行选门,计算柱子与残缺的关系
    {
        sort(men+1,men+1+hm,cmp);
        mmen.x=men[1].x;mmen.y=men[1].y;mmen.z=men[1].z;
        if(!mp[mmen.x][mmen.y][mmen.z^1])emm=1;
        if(!emm)bz++;
        if(mmen.x==hr+1||mmen.x==hr+hx-2)
        {
         int ying;
         if(mmen.x==hr+1)ying=hr;
         if(mmen.x==hr+hx-2) ying=hr+hx-1;
         for(int k=0;k<=1;k++)
          if(!mp[ying][mmen.y][k]&&!mp[mmen.x][mmen.y][k])cq++,zz--;
         }
        else if(mmen.y==hc+1||mmen.y==hc+hy-2)
        {
         int ying;
         if(mmen.y==hc+1)ying=hc;
         if(mmen.y==hc+hy-2)ying=hc+hy-1;
         for(int k=0;k<=1;k++)
             if(!mp[mmen.x][ying][k]&&!mp[mmen.x][mmen.y][k])cq++,zz--;
         }
         for(int i=1;i<=hm;i++)//把没有作为门的备选方案统计到残缺里面
         {
        if(men[i].x==mmen.x&&men[i].y==mmen.y)continue;
        cq++;
         }
    }
        if(bz<cq)
        {
        printf("SORRY CIRNO,NOT ENOUGH ICE_BLOCKS TO FIX THE WALL\n");
        return ;
    }
    printf("GOOD JOB CIRNO,SUCCESSFULLY BUILT THE HOUSE\n");
    if(!emm)
     printf("HOUSE HAS NO DOOR\n");
    else printf("DOOR IS OK\n");
    if(cq) printf("WALL NEED TO BE FIXED\n");
    else printf("WALL IS OK\n");
    if(zz) printf("CORNER NEED TO BE FIXED\n");
    else printf("CORNER IS OK\n");
    bz-=cq;bz-=zz;bz=max(bz,0);
    printf("CIRNO FINALLY HAS %d ICE_BLOCK(S)\n",bz);
    if(!yy&&!nie&&!cq&&emm&&!zz&&clo(mmen.x,mmen.y)==0)printf("CIRNO IS PERFECT!\n");
}

还是贴一下整个题的代码叭(友情提醒:没有注释,懒得调码风,maybe可以跳过)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map>
#include<vector>
#include<cmath>
using namespace std;
inline int read()
{
    char ch=getchar();
    int x=0;bool f=0;
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') f=1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<3)+(x<<1)+(ch^48);
        ch=getchar();
    }
    return f?-x:x;
}
const int N=309;
int jfz;
int n,m,ld[N][N],hr,hc,hx,hy,bz,hm,mp[N][N][N];
int mxh,tp;
bool ok(int x,int y)
{
    if(ld[x][y]<4)return true;
    return false;
}
void ice_barrage(int r,int c,int d,int s)
{
    int dong=0;
    for(int i=0;i<=s;i++)
    {
        int gx,gy;
        if(!d) gx=r-i,gy=c;
        if(d==1)gx=r-i,gy=c-i;
        if(d==2)gx=r,gy=c-i;
        if(d==3)gx=r+i,gy=c-i;
        if(d==4)gx=r+i,gy=c;
        if(d==5)gx=r+i,gy=c+i;
        if(d==6)gx=r,gy=c+i;
        if(d==7)gx=r-i,gy=c+i;
        if(gx<0||gy<0)break;
        if(gx>=n||gy>=n)break;
        if(ld[gx][gy]==20)break;
        if(ok(gx,gy))ld[gx][gy]++,dong++;
    }
    printf("CIRNO FREEZED %d BLOCK(S)\n",dong);
}
void make_ice_block()
{
    int co=0;
    for(int i=0;i<n;i++)
     for(int j=0;j<n;j++)
      {
        if(ld[i][j]==4)bz++,co++,ld[i][j]=0;
      }
    printf("CIRNO MADE %d ICE BLOCK(S),NOW SHE HAS %d ICE BLOCK(S)\n",co,bz);
}
bool sl(int r,int c,int h)
{
    if(r-1>=0&&mp[r-1][c][h])return false;
    if(r+1<n&&mp[r+1][c][h])return false;
    if(c-1>=0&&mp[r][c-1][h])return false;
    if(c+1<n&&mp[r][c+1][h])return false;
    if(h-1>=0&&mp[r][c][h-1])return false;
    if(h+1<n&&mp[r][c][h+1])return false;
    return true;
}
void put_ice_block(int r,int c,int h)
{
    if(!bz)
    {
        printf("CIRNO HAS NO ICE_BLOCK\n");
        return;
    }
    if(mp[r][c][h]||(h>0&&sl(r,c,h)))
    {
        printf("BAKA CIRNO,CAN'T PUT HERE\n");
        return ;
    }
    mp[r][c][h]=1;bz--;
    if(!h) ld[r][c]=20;
    if(r<hr||r>hr+hx-1||c<hc||c>hc+hy-1)
      printf("CIRNO MISSED THE PLACE\n");
    else if(hr+1<=r&&r<=hr+hx-2&&hc+1<=c&&c<=hc+hy-2)
      printf("CIRNO PUT AN ICE_BLOCK INSIDE THE HOUSE\n");
    else printf("CIRNO SUCCESSFULLY PUT AN ICE_BLOCK,NOW SHE HAS %d ICE_BLOCK(S)\n",bz);
}
struct dl{
    int x,y,z;
    dl(int xx,int yy,int zz):x(xx),y(yy),z(zz){}
};
struct ddl{
    int x,y,z;
}men[109];
queue <dl> q,as;
int dx[6]={-1,1,0,0,0,0},dy[6]={0,0,-1,1,0,0},dz[6]={0,0,0,0,-1,1};
bool vis[N][N][N];
bool jr(int x,int y,int z)
{
    if(vis[x][y][z]||(!mp[x][y][z]))return false;
    if(x>=n||y>=n||z>=hm)return false;
    if(x<0||y<0||z<0)return false;
    return true;
}
int bfs(int r,int c,int h)
{
    if(vis[r][c][h]||r<0||r>=n||c<0||c>=n||h<0||h>=hm)return 0;
    if(!mp[r][c][h])return 0;
    while(!q.empty())q.pop();
    while(!as.empty())as.pop();
    q.push(dl(r,c,h));as.push(dl(r,c,h));
    bool bj=0;//0:meiyoudimian,1:youdimian
    if(h==0)bj=1;
    vis[r][c][h]=1;
    while(!q.empty())
    {
        dl ex=q.front();
        q.pop();
        for(int i=0;i<6;i++)
        {
            int xx=ex.x+dx[i],yy=ex.y+dy[i],zz=ex.z+dz[i];
            if(jr(xx,yy,zz))
            {
                if(!zz)bj=1;
                q.push(dl(xx,yy,zz));vis[xx][yy][zz]=1;
                if(!bj)
                 as.push(dl(xx,yy,zz));
            }
        }
    }
    if(bj) return 0;
    int cnt=0;
    while(!as.empty())
    {
        dl ex=as.front();as.pop();
        cnt++;mp[ex.x][ex.y][ex.z]=0;
    }
    return cnt;
}
void remove_ice_block(int r,int c,int h)
{
    if(!mp[r][c][h])
    {
        printf("BAKA CIRNO,THERE IS NO ICE_BLOCK\n");
        return ;
    }
    mp[r][c][h]=0;bz++;
    if(!h)ld[r][c]=0;
    memset(vis,0,sizeof(vis));
    int ss=bfs(r+1,c,h)+bfs(r-1,c,h)+bfs(r,c+1,h)+bfs(r,c-1,h)+bfs(r,c,h+1)+bfs(r,c,h-1);
    if(ss)
     printf("CIRNO REMOVED AN ICE_BLOCK,AND %d BLOCK(S) ARE BROKEN\n",ss);
    else printf("CIRNO REMOVED AN ICE_BLOCK\n");
}
bool t1,t2,t3;
bool wai(int x,int y,int z)
{
    if((x<hr)||x>hr+hx-1)return true;
    if((y<hc)||(y>hc+hy-1))return true;
    if(z>mxh+1)return true;
    return false;
}
bool nei(int x,int y,int z)
{
    if((x>hr)&&(x<hr+hx-1)&&(y>hc)&&(y<hc+hy-1)&&(z<=mxh))return true;
    return false;
}
bool yy,nie,roof;
void make_roof()
{
    for(int i=hc;i<hc+hy;i++)
     for(int k=hm;k>=0;k--)
     {
       if(mp[hr][i][k]){mxh=max(mxh,k);break;}
       if(mp[hr+hx-1][i][k]) {mxh=max(k,mxh);break;}
     }
    for(int i=hr+1;i<hr+hx-1;i++)
     for(int k=hm;k>=0;k--)
     {
        if(mp[i][hc][k]){mxh=max(mxh,k);break;}
        if(mp[i][hc+hy-1][k]){mxh=max(mxh,k);break;}
     }
    int cnt=0;roof=1;
    for(int i=hr;i<hr+hx;i++)
     for(int j=hc;j<hc+hy;j++)
      if(!mp[i][j][mxh+1])cnt++,mp[i][j][mxh+1]=1;
    if(bz<cnt)
    {
        t1=1;printf("SORRY CIRNO,NOT ENOUGH ICE_BLOCK(S) TO MAKE ROOF\n");return ;
    }
    if(mxh<1||hx<3||hy<3)
    {
        t2=1;printf("SORRY CIRNO,HOUSE IS TOO SMALL\n");return ;
    }
    bz-=cnt;
    int ne=0,wa=0;
    for(int i=0;i<n;i++)
      for(int j=0;j<n;j++)
        for(int k=0;k<=hm;k++)
          {
            if(mp[i][j][k]&&wai(i,j,k)) yy=1,bz++,mp[i][j][k]=0,wa++;
            if(mp[i][j][k]&&nei(i,j,k)) nie=1,bz++,mp[i][j][k]=0,ne++;
          }
    memset(vis,0,sizeof(vis));
    int mbl=bfs(hr,hc,mxh+1);
    printf("%d ICE_BLOCK(S) INSIDE THE HOUSE NEED TO BE REMOVED\n",ne);
    printf("%d ICE_BLOCK(S) OUTSIDE THE HOUSE NEED TO BE REMOVED\n",wa);
    if(mbl)
    {
        t3=1;printf("SORRY CIRNO,HOUSE IS BROKEN WHEN REMOVING BLOCKS\n");return ;
    }
}
int clo(int x,int y)
{
    int midx,midy,mx1=100000,my1=100000;if(hx%2) midx=hr+hx/2;if(hy%2) midy=hc+hy/2;if(!(hx%2))midx=hr+hx/2-1,mx1=midx+1;if(!(hy%2))midy=hc+hy/2-1,my1=midy+1;
    int rx=min(abs(midx-x),abs(mx1-x));
    int ry=min(abs(midy-y),abs(my1-y));
    return min(rx,ry);
}
int val(ddl a)
{
    int gao=0;
    if(!mp[a.x][a.y][0])gao++;
    if(!mp[a.x][a.y][1])gao++;
    if((a.x==hr+1&&a.y==hc)||(a.x==hr&&a.y==hc+1))
    {
        if(!mp[hr][hc][0])gao--;
        if(!mp[hr][hc][0])gao--;
    }
    if((a.x==hr&&a.y==hc+hy-2)||(a.x==hr+1&&a.y==hc+hy-1))
    {
        if(!mp[hr][hc+hy-1][0])gao--;
        if(!mp[hr][hc+hy-1][1])gao--;
    }
    if((a.x==hr+hx-2&&a.y==hc+hy-1)||(a.x==hr+hx-1&&a.y==hc+hy-2))
    {
        if(!mp[hr+hx-1][hc+hy-1][0])gao--;
        if(!mp[hr+hx-1][hc+hy-1][1])gao--;
    }
    if((a.x==hr+hx-2&&a.y==hc)||(a.x==hr+hx-1&&a.y==hc+1))
    {
        if(!mp[hr+hx-1][hc][0])gao--;
        if(!mp[hr+hx-1][hc][1])gao--;
    }
    return gao;
}
bool cmp(ddl a,ddl b)
{
    int kva=1,kvb=1;if(a.z)kva=0;if(b.z)kvb=0;
    if(!mp[a.x][a.y][kva]&&mp[b.x][b.y][kvb])return 1;
    if(mp[a.x][a.y][kva]&&!mp[b.x][b.y][kvb])return 0;
    int va=val(a),vb=val(b);
    if(va!=vb) return va>vb;
    return clo(a.x,a.y)<clo(b.x,b.y);
}
void perfect()
{
    if(t1||t2||t3||!roof)return ;
    int hm=0,cq=0,zz=0;
    for(int i=hc;i<hc+hy;i++)
      for(int k=0;k<=mxh;k++)
      {
        if(!mp[hr][i][k])
        {
            if(k>1&&i!=hc&&i!=hc+hy-1)cq++;
            else if(i==hc||i==hc+hy-1) zz++;
            else men[++hm].x=hr,men[hm].y=i,men[hm].z=k;
        }
        if(!mp[hr+hx-1][i][k])
        {
            if(k>1&&i!=hc&&i!=hc+hy-1)cq++;
            else if(i==hc||i==hc+hy-1) zz++;
            else men[++hm].x=hr+hx-1,men[hm].y=i,men[hm].z=k;
        }
      }
    for(int i=hr+1;i<hr+hx-1;i++)
      for(int k=0;k<=mxh;k++)
      {
        if(!mp[i][hc][k])
        {
            if(k>1&&i!=hr&&i!=hr+hx-1)cq++;
            else men[++hm].x=i,men[hm].y=hc,men[hm].z=k;
        }
        if(!mp[i][hc+hy-1][k])
        {
            if(k>1&&i!=hr&&i!=hr+hx-1)cq++;
            else men[++hm].x=i,men[hm].y=hc+hy-1,men[hm].z=k;
        }
      }
    int emm=0; ddl mmen;
    if(hm)
    {
        sort(men+1,men+1+hm,cmp);
        mmen.x=men[1].x;mmen.y=men[1].y;mmen.z=men[1].z;
        if(!mp[mmen.x][mmen.y][mmen.z^1])emm=1;
        if(!emm)bz++;
        if(mmen.x==hr+1||mmen.x==hr+hx-2)
        {
         int ying;
         if(mmen.x==hr+1)ying=hr;
         if(mmen.x==hr+hx-2) ying=hr+hx-1;
         for(int k=0;k<=1;k++)
          if(!mp[ying][mmen.y][k]&&!mp[mmen.x][mmen.y][k])cq++,zz--;
        }
        else if(mmen.y==hc+1||mmen.y==hc+hy-2)
        {
         int ying;
         if(mmen.y==hc+1)ying=hc;
         if(mmen.y==hc+hy-2)ying=hc+hy-1;
         for(int k=0;k<=1;k++)
             if(!mp[mmen.x][ying][k]&&!mp[mmen.x][mmen.y][k])cq++,zz--;
        }
        for(int i=1;i<=hm;i++)
        {
            if(men[i].x==mmen.x&&men[i].y==mmen.y)continue;
            cq++;
        }
    }
      if(bz<cq)
      {
        printf("SORRY CIRNO,NOT ENOUGH ICE_BLOCKS TO FIX THE WALL\n");
        return ;
    }
    printf("GOOD JOB CIRNO,SUCCESSFULLY BUILT THE HOUSE\n");
    if(!emm)
     printf("HOUSE HAS NO DOOR\n");
    else printf("DOOR IS OK\n");
    if(cq) printf("WALL NEED TO BE FIXED\n");
    else printf("WALL IS OK\n");
    if(zz) printf("CORNER NEED TO BE FIXED\n");
    else printf("CORNER IS OK\n");
    bz-=cq;bz-=zz;bz=max(bz,0);//bz++;
    printf("CIRNO FINALLY HAS %d ICE_BLOCK(S)\n",bz);
    if(!yy&&!nie&&!cq&&emm&&!zz&&clo(mmen.x,mmen.y)==0)printf("CIRNO IS PERFECT!\n");
}
int main()
{
    n=read();hm=read();hr=read();hc=read();hx=read();hy=read();
    m=read();tp=m;
    while(m--)
    {
       char a[20];scanf("%s",a);
       if(a[0]=='I')
       {
        int e=read(),f=read(),g=read(),h=read();
        ice_barrage(e,f,g,h);
       }
       if(a[0]=='M'&&a[5]=='I')
        make_ice_block();
       if(a[0]=='P')
       {
        int r=read(),c=read(),h=read();
        put_ice_block(r,c,h);
       }
      if(a[0]=='R')
       {
        int r=read(),c=read(),h=read();
        remove_ice_block(r,c,h);
       }
       if(a[0]=='M'&&a[5]=='R')
        make_roof();
    }
    perfect();
    return 0;
}
12-30 23:30