一个N*N的网格,初始为白色。现在有一个K*K的印章,每次操作:你可以用印章把网格中一个K*K的子矩形染成黑色或白色。如果一个格子被多次染色,那么后一次染色会覆盖掉前一次的。现在,给你N*N的由黑白两色构成的图案board(board[i][j]为第i行第j列格子的颜色,不是白字母‘W’表示,就是黑由字母‘B’表示),问是否能通过若干次操作,将网格从初始状态染成图案board的样子。如果可以输出"Possible",否则输出"Impossible".
Input
多组测试数据,第一行一个整数T,表示测试数据数量,1<=T<=5 每组测试数据有相同的结构构成: 每组数据一行两个整数N与K,其中1<=K<=N<=20。 之后N行,每行N个字符,表示board,字符都由‘W’与‘B’构成。
Output
每组数据一行输出,即是否可能染出board图案来。
Input示例
3 4 3 BBBW BWWW BWWW WWWW 2 2 BW WB 6 2 BWBWBB WBWBBB BWBWBB WBWBBB BBBBBB BBBBBB
Output示例
Possible Impossible Possible
官方题解:
可以尝试逆向思维,最后一块无论是黑是白,都一定是k*k的连通块。倒数第i块,其一部分被最后的i-1块遮挡,其余部分必然同色且分布在k*k的矩形内。因此可以逆向贪心构造,直到每一个小正方形被覆盖为止。
其实这个题就像以前做过的一个贴海报的题目,首先确定一个全部为同色的k*k的块,然后将这些块内的格子标记为访问过了,对于后面的k*k的块,这时候只需要判断除了已经访问的点外,其他的是否为同色,知道找不到这样的块为止,到最后再扫一遍,若存在没被访问的且为black的就说明不存在解决方案。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const ll mod=1000000007;
const int maxn=20+10;
char s[maxn][maxn];
bool vis[maxn][maxn];
int main()
{
int cas;
scanf("%d",&cas);
while(cas--)
{
memset(vis,false,sizeof(vis));
int n,k;
scanf("%d%d",&n,&k);
bool flag=true;
for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
while(flag)
{
flag=false;
for(int i=1;i<=n-k+1;i++)
{
for(int j=1;j<=n-k+1;j++)
{
int sum1=0,sum2=0;
bool f=false;
for(int ii=i;ii<i+k;ii++)
{
for(int jj=j;jj<j+k;jj++)
{
if(vis[ii][jj]) continue;
f=true;
if(s[ii][jj]=='B') sum1++;
else sum2++;
}
}
if((sum1==0||sum2==0)&&f)
{
flag=true;
for(int ii=i;ii<i+k;ii++)
{
for(int jj=j;jj<j+k;jj++)
vis[ii][jj]=true;
}
}
}
}
}
bool f=true;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(!vis[i][j]&&s[i][j]=='B') f=false;
}
if(f) puts("Possible");
else puts("Impossible");
}
return 0;
}