题目链接
http://main.edu.pl/en/user.phtml?op=showtask&task=pie&con=OI22
题目大意
一张n*m的方格纸,有些格子需要印成黑色,剩下的格子需要保留白色。
你有一个a*b的印章,有些格子是凸起(会沾上墨水)的。你需要判断能否用这个印章印出纸上的图案。印的过程中需要满足以下要求:
(1)印章不可以旋转。
(2)不能把墨水印到纸外面。
(3)纸上的同一个格子不可以印多次。
对于每个测试点,输出TAK(是)或NIE(否)。
思路
我们找出印章里最左上角的凸起的点,记录下其他凸起的点和这个点之间的相对位置。然后再在方格纸里不断枚举需要被印刷的图案的左上角的黑点,每次枚举出一个黑点,就用之前所求的相对位置把附近的黑点标记为被染过色。反复这样做。这个过程中如果发现有不合法的情况,就说明无法给这个方格纸染色了
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 1100
using namespace std;
int n,m,a,b;
char mp[MAXN][MAXN],stamp[MAXN][MAXN];
pair<int,int>stack[MAXN*MAXN];
int top=0;
bool vis[MAXN][MAXN];
int T;
bool inMap(int i,int j)
{
if(i<=0||i>n||j<=0||j>m) return false;
if(vis[i][j]||mp[i][j]=='.') return false;
return true;
}
int main()
{
scanf("%d",&T);
while(T--)
{
top=0;
scanf("%d%d%d%d",&n,&m,&a,&b);
for(int i=1;i<=n;i++)
scanf("%s",mp[i]+1);
for(int i=1;i<=a;i++)
scanf("%s",stamp[i]+1);
int zsx=-1,zsy=-1; //印章左上角突起的点d坐标
for(int i=1;i<=a;i++)
for(int j=1;j<=b;j++)
{
if(stamp[i][j]=='x')
{
if(zsx==-1)
{
zsx=i;
zsy=j;
}
else
stack[++top]=make_pair(i-zsx,j-zsy);
}
}
if(zsx==-1)
{
printf("NIE\n");
continue;
}
memset(vis,0,sizeof(vis));
bool flag=false;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(mp[i][j]=='.'||vis[i][j]) continue;
vis[i][j]=true;
for(int k=1;k<=top;k++)
{
if(!inMap(i+stack[k].first,j+stack[k].second))
{
flag=true;
break;
}
vis[i+stack[k].first][j+stack[k].second]=true;
}
if(flag) break;
}
if(flag) break;
}
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)
{
if(vis[i][j]==0&&mp[i][j]=='x')
{
flag=true;
break;
}
}
if(flag)
printf("NIE\n");
else printf("TAK\n");
}
return 0;
}