P10257 [COCI 2023/2024 #5] Zlagalica 题解
题目大意:
给你 \(n\) 块拼图,颜色为 \(b_i\),所占的行数和列数分别为 \(r_i\) 和 \(s_i\),按照给定的顺序,以及在上一块拼图的指定方向去拼(只有向上或者向右),求最终拼出来的图案。
题目分析:
(注:下文形如 \((x,y)\) 的坐标表示第 \(x\) 行,第 \(y\) 列!)
本题为小模拟。
观察到 \(1 \leq n \leq 20,1 \leq r_i,s_i \leq 10\),数据范围较小,最大开 \(200 \times 200\) 的数组存答案即可,直接模拟摆放的操作即可。由于每次拼的拼图只会拼在上一个拼图的上面或右面且紧挨,所以可以考虑从右下角 \((200,1)\) 开始模拟,这样就不用考虑什么翻转数组之类的东西了,最后再判断该行 / 该列有拼图再开始输出即可。
这里我选择记录每个拼图的右下角,即 \((x_{now},y_{now})\),初始为 \((200,1)\)。
时间复杂度:\(O(n)\)。
Code:
#include<bits/stdc++.h>
using namespace std;
const int N=25;
int n,X[N*10],Y[N*10],bx,ey;//bx 开始行,ey结束列,因为从 (200,1) 开始模拟
char ans[N*10][N*10];//答案拼图
struct dat
{
char ch;//颜色
int x,y,u,d;//行数,列数,方向,拼在对于上一块的第d行/列
}a[N],now;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i].ch>>a[i].x>>a[i].y>>a[i].u>>a[i].d;//读入拼图
now.x=200,now.y=1;
memset(ans,'.',sizeof(ans));//初始化答案
int u=0,last=0;//u为当前拼图,last为上一次的拼图,初始为0
while(n--)
{
cin>>u;
if(last)
{
if(a[last].u==0)//向上摆
{
now.x-=a[last].x;
now.y+=a[last].d-1;
}
else//向右摆
{
now.y+=a[last].y;
now.x+=-a[last].x+a[last].d;
}
}
for(int i=now.x-a[u].x+1;i<=now.x;i++)
for(int j=now.y;j<=now.y+a[u].y-1;j++)
ans[i][j]=a[u].ch;//覆盖答案
last=u;//更新上一块积木的编号
}
for(int i=1;i<=200;i++)
for(int j=1;j<=200;j++)
if(ans[i][j]=='.') X[i]++,Y[j]++;//记录该行 / 该列是否有积木
for(int i=1;i<=200;i++)//从上往下遍历
if(X[i]!=200)
{
bx=i;//记录答案开始行
break;
}
for(int i=200;i>=1;i--)//从右往左遍历
if(Y[i]!=200)
{
ey=i;//记录答案的结束列
break;
}
printf("%d %d\n",200-bx+1,ey);//输出答案大小
for(int i=bx;i<=200;i++)
{
for(int j=1;j<=ey;j++)
printf("%c",ans[i][j]);//输出拼图图案
printf("\n");
}
return 0;
}