#dp#CodeChef Little Elephant and Mouses
阿新 • • 發佈:2022-03-14
分析
由於被單隻老鼠嚇到只能算一次,所以前兩次走的位置也可能會被老鼠嚇到。
設 \(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; }