[JOISC2016]サンドイッチ
题目大意:
一个$n\times m(n,m\leq400)$的网格图中,每个格子上放了两个三明治,摆放的方式分为'N'和'Z'两种。一个三明治可以被拿走当且仅当与该三明治的两条直角边相邻的三明治均被拿走或与该三明治斜边相邻的三明治被拿走。问对于每个格子,想要拿走这个格子中的所有三明治至少需要先拿走多少三明治。
思路:
对于同一个格子,不难发现这一个格子中两个三明治接连被拿走一定是最优的。
于是这题就和每个单独的三明治取走顺序没什么关系了,只和每个方格取走顺序及三明治的摆放方式有关。
$O(n^2)$枚举每个格子$(x,y)$,假设它是因为$(x-1,y)$和$(x,y-1)$被取走后才被取走,我们可以$O(n^2)$DFS出最优情况下,取走每个格子之前一定要取走哪些格子。时间复杂度$O(n^4)$,bitset优化为$O(\frac{n^4}{\omega})$。
不难发现,若$(x,y)$是因为$(x-1,y)$被取走才被取走的,$(x-1,y)$不可能因为$(x,y)$被取走才被取走。因此对于同一行的格子,我们可以让后面的DFS重复利用前面DFS出的信息。DFS是$O(n^2)$的,每一行要重新DFS,时间复杂度是$O(n^3)$。
具体实现上,可以用$0\sim3$来表示不同的方向。若摆放方式为'N'的格子,一个直角边的方向为$d$,则另一个直角边的方向为$d\oplus3$;若摆放方式为'Z'的格子,一个直角边的方向为$d$,则另一个直角边的方向为$d\oplus1$。搜索时的一系列分类讨论可以通过简单的位运算实现。
1 #include<cstdio> 2 #include<cctype> 3 #include<algorithm> 4 inline int getint() { 5 register char ch; 6 while(!isdigit(ch=getchar())); 7 register int x=ch^'0'; 8 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 9 return x; 10 } 11 inline bool getblock() { 12 register char ch; 13 while(!isalpha(ch=getchar())); 14 return ch=='N'; 15 } 16 const int N=400,inf=0x7fffffff; 17 const int dx[]={0,-1,0,1},dy[]={-1,0,1,0}; 18 bool a[N][N]; 19 int n,m,f[N][N],ans[N][N],tmp,flag; 20 inline bool check(const int &x,const int &y) { 21 return x>=0&&x<n&&y>=0&&y<m; 22 } 23 void dfs(const int &x,const int &y,const int &d) { 24 if(!~f[x][y]) { 25 flag=true; 26 return; 27 } 28 if(f[x][y]) return; 29 tmp+=2; 30 f[x][y]=-1; 31 const int p=a[x][y]?3:1; 32 if(check(x-dx[d],y-dy[d])) dfs(x-dx[d],y-dy[d],d); 33 if(check(x-dx[d^p],y-dy[d^p])) dfs(x-dx[d^p],y-dy[d^p],d^p); 34 f[x][y]=1; 35 } 36 int main() { 37 for(register int T=getint();T;T--) { 38 n=getint(),m=getint(); 39 for(register int i=0;i<n;i++) { 40 for(register int j=0;j<m;j++) { 41 a[i][j]=getblock(); 42 } 43 } 44 for(register int i=0;i<n;i++) { 45 flag=tmp=0; 46 for(register int i=0;i<n;i++) { 47 for(register int j=0;j<m;j++) { 48 f[i][j]=0; 49 } 50 } 51 for(register int j=0;j<m;j++) { 52 if(!flag) dfs(i,j,2); 53 ans[i][j]=flag?inf:tmp; 54 } 55 flag=tmp=0; 56 for(register int i=0;i<n;i++) { 57 for(register int j=0;j<m;j++) { 58 f[i][j]=0; 59 } 60 } 61 for(register int j=m-1;~j;j--) { 62 if(!flag) dfs(i,j,0); 63 ans[i][j]=std::min(ans[i][j],flag?inf:tmp); 64 } 65 } 66 for(register int i=0;i<n;i++) { 67 for(register int j=0;j<m;j++) { 68 printf("%d%c",ans[i][j]!=inf?ans[i][j]:-1," \n"[j==m-1]); 69 } 70 } 71 } 72 return 0; 73 }