1. 程式人生 > 其它 >#dp#CodeChef Little Elephant and Mouses

#dp#CodeChef Little Elephant and Mouses

LEMOUSE


分析

由於被單隻老鼠嚇到只能算一次,所以前兩次走的位置也可能會被老鼠嚇到。

\(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;
}