问题描述
小 G 的喜欢作画, 尤其喜欢仅使用黑白两色作画.
画作可以抽象成一个 r×c 大小的 01 矩阵. 现在小 G 构思好了了他的 画作, 准备动笔开始作画. 初始时画布是全白的, 他每一次下笔可以将一个 四联通的部分涂成黑色或白色.
你需要告诉他, 在给出的构思下, 他最少需要下笔多少次才能完成画作.
输入格式
第一行两个正整数 r,c. 接下来 r 行, 每行 c 个字符, 表示目标画作.
输出格式
输出一行一个正整数, 表示最少需要的下笔步数.
结论题 ??
不难证明猜到一个这样的结论: 存在一种最优方案使得每次操作的区
域是上一次的子集且颜色与上一次相反.
考虑归纳证明, 记S 为当前所有操作区域的并, T 为接下来一步的操作
区域, 我们有:
- T 与S 有交的情况一定可以转化成T 被S 包含的情况.
- T 与S 交集为空时, 可以找一个连接S 和T 的集合M 并操作S [
T [M, 并将之前的所有操作连接到更外的层以及外层的连接部分同时
操作, 特殊处理最外层和第二层的情况.- T 被S 包含时, T 落在某个完整区域内时等价于情况二, 否则一定连
接若干个同色块, 这些块可以同时处理, 步数一定不会更劣.
知道这个结论就比较好做了, 我们可以枚举最后被修改的区域, 这时答
案就是将同色边边权当作0, 异色边边权当作1 后距离这个点最远的黑色点
的距离, 对所有点取最小值即可.
01BFS真好玩
AC Code:
#include<bits/stdc++.h>
#define maxn 55
using namespace std;
int n,m;
int dis[maxn][maxn],vis[maxn][maxn],tim;
int q1[maxn*maxn*2],q2[maxn*maxn*2],L,R;
char Map[maxn][maxn];
int dir[4][2] = {{1,0},{0,1},{0,-1},{-1,0}};
inline bool check(int u,int v){ return u>-1 && u<=n && v>=1 && v<=m; }
int main()
{
freopen("paint.in","r",stdin);
freopen("paint.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%s",Map[i]+1);
int ans = 0x3f3f3f3f;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
tim++;L=R=2500;
vis[i][j] = tim ; dis[i][j] = 0;
q1[R] = i , q2[R++] = j;
int ret = 0;
for(int x,y;L<R;)
{
x = q1[L] , y = q2[L++];
if(Map[x][y]=='1')ret = max(ret , dis[x][y]);
for(int k=0,u,v;k<4;k++)
if(check(u = x + dir[k][0] , v= y +dir[k][1]) && vis[u][v]!=tim)
{
vis[u][v] = tim;
dis[u][v] = dis[x][y] + (Map[x][y] != Map[u][v]);
if(Map[x][y] != Map[u][v]) q1[R] = u , q2[R++] = v;
else q1[--L] = u , q2[L] = v;
}
}
ans = min(ans , ret);
}
printf("%d\n",ans+1);
}