【2018 国庆雅礼 NOIP 培训】D1T3 画作(paint)

问题描述
小 G 的喜欢作画, 尤其喜欢仅使用黑白两色作画.
画作可以抽象成一个 r×c 大小的 01 矩阵. 现在小 G 构思好了了他的 画作, 准备动笔开始作画. 初始时画布是全白的, 他每一次下笔可以将一个 四联通的部分涂成黑色或白色.
你需要告诉他, 在给出的构思下, 他最少需要下笔多少次才能完成画作.
输入格式
第一行两个正整数 r,c. 接下来 r 行, 每行 c 个字符, 表示目标画作.
输出格式
输出一行一个正整数, 表示最少需要的下笔步数.

结论题 ??

不难证明猜到一个这样的结论: 存在一种最优方案使得每次操作的区
域是上一次的子集且颜色与上一次相反.
考虑归纳证明, 记S 为当前所有操作区域的并, T 为接下来一步的操作
区域, 我们有:

  1. T 与S 有交的情况一定可以转化成T 被S 包含的情况.
  2. T 与S 交集为空时, 可以找一个连接S 和T 的集合M 并操作S [
    T [M, 并将之前的所有操作连接到更外的层以及外层的连接部分同时
    操作, 特殊处理最外层和第二层的情况.
  3. 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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值