CF115B 题解

题目大意

你有一台割草机可以割除所有的杂草。最初,你站在 n × m ( n , m ≤ 150 ) n \times m(n,m \le 150) n×m(n,m150) 的花园的左上角(即在方格(1,1)处),花园的每一个方格用 a i , j a_{i,j} ai,j 表示。在任何时刻,你都面临着某个方向——左或右。最初,你面对右。

在一个步骤中,您可以执行以下任一操作:

  1. 向面向的方向移动一格
  2. 向下移动一格,并同时改变方向

如果你和你的割草机站在含有杂草的方格中(你的方向无关紧要),杂草就会被修剪掉。此操作不算作动作。问割除所有杂草所需的最小移动次数是多少?

杂草用 W 表示

样例输入

4 5
GWGGW
GGWGG
GWGGG
WGGGG

样例输出

11

样例解释

在这里插入图片描述

思路

显而易见,我们的路线图一定是一个类似蛇形的形状。而我们将其分成横向移动(不改变方向) 与纵向移动。

横向移动

对于每一行的最左边一个含有 w 的点(记作 l i l_i li)以及 每一行的最右边一个含有 w 的点(记作 r i r_i ri),显而易见 l i l_i li r i r_i ri 必然有一个是另一个得来,因为已经到了第 i i i 行不可能再向上移动。因此对于每一行,我们只需要考虑最外侧的两个为 W 的点,而无需考虑中间的 W

我们用 y y y 表示现在在第 y y y 列, a n s ans ans 表示现在移动了 a n s ans ans 格。显而易见,如果现在是奇数排( i m o d    2 = 1 i \mod 2 = 1 imod2=1),那么朝右移动到 y 1 y1 y1。则 a n s ← a n s + y 1 − y , y ← y 1 ans \gets ans + y1 - y,y \gets y1 ansans+y1y,yy1;否则,则向左移动到 y 1 y1 y1 a n s ← a n s + y − y 1 , y ← y 1 ans \gets ans + y - y1,y \gets y1 ansans+yy1,yy1。显而易见, y 1 y1 y1 一定不会小于 r i r_i ri,因此 y 1 ← r i y1 \gets r_i y1ri

加入纵向移动后的影响

考虑以下情况

2 4
GGWG
GGGW

如果我们移动到 r 1 r_1 r1 再向下,那么第二行的最右边无法割除。所以在 i m o d    2 = 1 i \mod 2 = 1 imod2=1 时, y 1 ← max ⁡ ( r i , r i + 1 ) y1 \gets \max(r_i,r_i + 1) y1max(ri,ri+1),否则 y 1 ← min ⁡ ( l i , l i + 1 ) y1 \gets \min(l_i,l_i + 1) y1min(li,li+1)。因此,我们预处理 l , r l,r l,r 时,一定要注意。同时记得移动到下一行时记得 a n s ← a n s + 1 ans \gets ans + 1 ansans+1

坑点

  • 如果接下来的行中没有 W,就直接输出当前答案,不需要再向下。
// 判断函数
bool check(int x) {
	for(int i = x;i <= n;i++) if(r[i]) return false;
	return true;
} 

  • 有时候在偶数行,会出现 m i n ( l 2 i , l 2 i + 1 ) > y min(l_{2i},l_{2i + 1}) > y min(l2i,l2i+1)>y 的情况,如以下情况的第四行:
5 4
GGGW
GGGG
WGGG
GGGG
GGWG

因此,我们最好要让 y 1 = m i n ( y , l 2 i , l 2 i + 1 ) y1 = min(y,l_{2i},l_{2i + 1}) y1=min(y,l2i,l2i+1)
奇数行同理。

代码

#include<bits/stdc++.h>
using namespace std;
int l[155],r[155];
int n,m;
char a[155][155];
bool check(int x) {
	for(int i = x;i <= n;i++) if(r[i]) return false;
	return true;
} 
int main() {
	int ans = 0,y1,y = 1;
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n;i++) {
		l[i] = m,r[i] = 0;
		for(int j = 1;j <= m;j++) cin >> a[i][j];
		for(int j = 1;j <= m;j++) if(a[i][j] == 'W') r[i] = j;
		for(int j = m;j >= 1;j--) if(a[i][j] == 'W') l[i] = j;
	}
	for(int i = 1;i < n;i++) {
		if(i & 1) {
			y1 = max(max(y,r[i]),r[i + 1]);
			ans += y1 - y;
			y = y1;
		}
		else {
			y1 = min(y,min(l[i],l[i + 1]));
			ans += y - y1;
			y = y1;
		}
		if(check(i + 1)) {
			printf("%d\n",ans);
			return 0;
		}
		ans++;
	}
	if(r[n]) {
		if(n & 1) ans += r[n] - y;
		else ans += y - l[n];
	}
	printf("%d\n",ans);
	return 0;
}
  • 18
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值