题目链接
https://loj.ac/problem/2733
题解
神仙题……
首先可以观察到一个结论: 目标块的两块小三明治一定分别是最后和倒数第二个被吃的。
由此我们可以考虑这两块谁先被吃。这样的好处就是,起初我们一个块被吃的依赖条件是某两个块中有一个被吃就行,现在两个块中的某一个已经钦定了比它更晚,另一个就一定要比它早,这样依赖关系就形成了一张图。
那么有一个\(O(n^4)\)的做法: 对于每一个块枚举先吃哪个小三明治,然后DFS求出要先吃这个三明治需要吃掉哪些三明治。
下面还有一个结论: 设对于一个块\((x,y)\) (第\(x\)行第\(y\)列)我们先吃掉了靠左边界的块,那么对于块\((x,y-1)\) (即它左边的块),我们也需要先吃掉靠左边界的块,右同理。
推论: 设\(L(x,y)\)是要先取走块\((x,y)\)靠左边界的块需要取走的块的集合,则\(L(x-1,y)\subset L(x,y)\).
于是枚举每一行,在这一行中从左到右DFS求\(L\), 从右往左DFS求\(R\), 遍历过的点无需再遍历。
总时间复杂度\(O(n^3)\).
代码
#include<bits/stdc++.h>
#define llong long long
#define mkpr make_pair
using namespace std;const int N = 400;
const int INF = 1e8;
char a[N+3][N+3];
int vis[N+3][N+3];
int dp0[N+3][N+3],dp1[N+3][N+3];
int n,m;int dfs(int x,int y,int dir)
{if(vis[x][y]==-1) {return INF;}else if(vis[x][y]==1) {return 0;}vis[x][y] = -1; int ret = 2;if(a[x][y]=='N'){if(dir==1){if(x>1) {ret += dfs(x-1,y,a[x-1][y]=='N'?1:0);}if(y<m) {ret += dfs(x,y+1,1);}}else{if(x<n) {ret += dfs(x+1,y,a[x+1][y]=='N'?0:1);}if(y>1) {ret += dfs(x,y-1,0);}}}else{if(dir==1){if(x<n) {ret += dfs(x+1,y,a[x+1][y]=='N'?0:1);}if(y<m) {ret += dfs(x,y+1,1);}}else{if(x>1) {ret += dfs(x-1,y,a[x-1][y]=='N'?1:0);}if(y>1) {ret += dfs(x,y-1,0);}}}if(ret<INF) {vis[x][y] = 1;}else ret = INF;return ret;
}int main()
{scanf("%d%d",&n,&m);for(int i=1; i<=n; i++) scanf("%s",a[i]+1);for(int i=1; i<=n; i++){memset(vis,0,sizeof(vis));for(int j=1; j<=m; j++){dp0[i][j] = dp0[i][j-1]+dfs(i,j,0);if(dp0[i][j]>INF) {dp0[i][j] = INF;}}memset(vis,0,sizeof(vis));for(int j=m; j>=1; j--){dp1[i][j] = dp1[i][j+1]+dfs(i,j,1);if(dp1[i][j]>INF) {dp1[i][j] = INF;}}for(int j=1; j<=m; j++){int ans = min(dp0[i][j],dp1[i][j]);printf("%d ",ans<INF?ans:-1);}puts("");}return 0;
}