这道题一开始以为是有规律的的, 想通过最大区域的0来计算,然后用bfs来解决,但是写到一半又发现思路错了,最后还是学长提醒我用dp思路去做,纪录最右和最左的点,dp已经很久没做了,看了网上其他题解才明白要怎么去维护那些状态,里面有几个点需要注意一下:
1.所有的数都为0
2.最下面楼层的dp方式不一样
3.每次dp要保存前一个的状态 因为下一次会变化
AC代码:
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
int dp[20][110];
char mapp[20][110];
int n,m;
int x,y;
int main() {
scanf("%d%d",&n,&m);
x = -1;
y = -1;
for(int i = 1; i <= n; i++) {
scanf("%s",mapp[i]+1);
if(x != -1 && y != -1) {
continue;
}
for(int j = 1; j <= m+2; j++) {
if(mapp[i][j] == '1') {
x = i;
y = j;
break;
}
}
}
//x纪录的是最上面没有关灯的层数
if(x == -1 && y == -1) {
printf("0\n");
}
else {
int l, r;
memset(dp, INF, sizeof(dp));
dp[n][1] = 0;
for(int i = n; i > 0; i--) {
dp[i][1] = min(dp[i][1], dp[i+1][1] + 1);
dp[i][m+2] = min(dp[i][m+2], dp[i+1][m+2] + 1);
l = 0;
r = 0;
for(int j = 2; j <= m+1; j++) {
if(mapp[i][j] == '1') {
if(!l) {
l = j;
}
r = j;
}
}
int w = dp[i][1];//记录
if(r) {
dp[i][1] = min(dp[i][1] + 2*(r-1), dp[i][m+2]+m+1);
//1 到最右边的 5 来回就是(5-1)*2 = 8 步数
}
if(l) {
dp[i][m+2] = min(dp[i][m+2] + 2*(m+2-l), w+m+1);
//最右的m+2位置到最左的位置为 ((m+2) - l)*2 步数
// 这里的w是之前标记的dp【i】【1】 因为上一个判断会让这个值发生变化
}
}
l = 0, r = 0;
for(int i = 1; i <= m+2; i++) {
if(mapp[x][i] == '1') {
if(!l) {
l = i;
}
r = i;
}
}
int ans = min(dp[x+1][1] + r, dp[x+1][m+2] + (m+2-l) + 1);
if(x == n) {
ans = r - 1;
}
printf("%d\n",ans);
}
return 0;
}