#dp#CodeChef Little Elephant and Mouses
分析
由于被单只老鼠吓到只能算一次,所以前两次走的位置也可能会被老鼠吓到。
设 \(dp[n][m][o][p]\) 表示走到 \((n,m)\) 上一步走的是 \(o\) 这种方式,再上一步走的是 \(p\) 这种方式的最小惊吓次数。
转移就直接判断一下是否作为第一次被吓到即可。
代码
#include <cstdio>
#include <cctype>
#include <cstring>
using namespace std;
const int N=111,dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
int n,m,v[N][N],dp[N][N][2][2],upd; char s[N][N];
int iut(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
void Min(int &x,int y){x=x<y?x:y;}
int calc(int x,int y,int zx,int zy,int Zx,int Zy){
int sum=0;
v[zx][zy]=v[Zx][Zy]=++upd;
for (int i=0;i<4;++i){
if (zx+dx[i]>0&&zy+dy[i]>0) v[zx+dx[i]][zy+dy[i]]=upd;
if (Zx+dx[i]>0&&Zy+dy[i]>0) v[Zx+dx[i]][Zy+dy[i]]=upd;
}
for (int i=0;i<4;++i)
if (s[x+dx[i]][y+dy[i]]==49&&v[x+dx[i]][y+dy[i]]!=upd)
++sum;
return sum;
}
int main(){
for (int T=iut();T;--T){
n=iut(),m=iut(),upd=0;
memset(v,0,sizeof(v));
memset(s,'\0',sizeof(s));
memset(dp,42,sizeof(dp));
for (int i=1;i<=n;++i) scanf("%s",s[i]+1);
dp[1][1][0][0]=dp[1][1][1][1]=(s[1][1]==49)+(s[1][2]==49)+(s[2][1]==49);
for (int i=2;i<=m;++i) dp[1][i][0][0]=dp[1][i-1][0][0]+(s[2][i]==49)+(s[1][i+1]==49);
for (int i=2;i<=n;++i) dp[i][1][1][1]=dp[i-1][1][1][1]+(s[i][2]==49)+(s[i+1][1]==49);
for (int i=2;i<=n;++i)
for (int j=2;j<=m;++j)
for (int o=0;o<2;++o)
for (int p=0;p<2;++p){
if (dp[i][j-1][o][p]!=dp[0][0][0][0])
Min(dp[i][j][0][o],dp[i][j-1][o][p]+calc(i,j,i,j-1,i-dx[o],j-1-dy[o]));
if (dp[i-1][j][o][p]!=dp[0][0][0][0])
Min(dp[i][j][1][o],dp[i-1][j][o][p]+calc(i,j,i-1,j,i-1-dx[o],j-dy[o]));
}
int ans=dp[0][0][0][0];
for (int o=0;o<2;++o)
for (int p=0;p<2;++p)
Min(ans,dp[n][m][o][p]);
printf("%d\n",ans);
}
return 0;
}