Blikewsr
2024-11-18 20:09:49
CF812B - Luogu
CF812B - Codeforces
给定一个楼层图,用 01
表示,1
表示开灯的房间,最左边和最右边为楼梯间,用 0
表示,我们在 最底层最左边 的楼梯口,求至少要走多少步才能关掉所有的灯。
注意到,最高几层可能全部是 0
,说明这几层没有灯,我们跟本不用走,所以我们可以先枚举每一层,找到 最高的且有灯开着的 一层开始走。
我们定义一个二维数组 cnt[N][2]
,其中
然后在定义一个两个一维数组 lef[N]
和 rig[N]
,其中
现在,我们来考虑以下转移方程。
显然的,每一层的步数只受它上一层的步数和本层亮灯的房间影响的。
其次,最优的走法肯定是要么走完一整层从另一边上到上一层,要么走到这一层里所在楼梯口最远的亮着灯的房间在原路返回在上到上一层,所以我们有
对于第
对于第
对于第
对于第
知道了所有的走法的转移方程,既然我们要求最小步数,那么我们直接让对应的转移方程分别取最小值就可以了。
即:
注意: 转移方程上面的加一是因为上楼区要在楼梯间走一步,所以 最高的且有灯开着的 那一层楼不用上楼。
其他细节见代码注释
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, m, h, mp[20][107], lef[20], rig[20];
int cnt[20][2];
char s[20][107];
signed main() {
cin >> n >> m;
for (int i = 1; i <= n; ++ i)
cin >> s[i]; // 因为输入没有空格,先用字符类型输入
for (int i = 1; i <= n; ++ i) {
lef[i] = m + 1, rig[i] = 0;
for (int j = 1; j <= m; ++ j) {
mp[i][j] = (int)(s[i][j] - '0');
if (mp[i][j]) {
if (lef[i] == m + 1) lef[i] = rig[i] = j; // 最左边亮着灯的房间
else rig[i] = j; // 最右边亮着灯的房间
if (!h) h = i; // 最高的且有灯开着的一层
}
}
}
if (!h) { // 整个楼房没有房间开灯,直接走人
cout << "0\n";
return 0;
}
cnt[h][0] = rig[h], cnt[h][1] = m - lef[h] + 1;
for (int i = h + 1; i <= n; ++ i) {
// 转移方程
cnt[i][0] = min(cnt[i - 1][0] + rig[i] * 2, cnt[i - 1][1] + m + 1);
cnt[i][1] = min(cnt[i - 1][1] + (m - lef[i] + 1) * 2, cnt[i - 1][0] + m + 1);
// 往上走一层
++ cnt[i][0];
++ cnt[i][1];
}
cout << cnt[n][0] << '\n'; // 因为在左侧,所以输出 cnt[n][0]
return 0;
}