P3473 [POI2008] UCI-The Great Escape 题解
发现正着不好做,考虑倒着做,即从 $(x,y)$ 开始走转弯只能向左转。
考虑怎么处理不重复走的限制。发现路径是螺旋形的,可以用一个矩形将路径框起来,那么每走一步都会使矩形的长或宽往某个方向延长一个单位。转弯可以看成向前走一格,再左转然后走到矩阵的边界。
然后就可以 dp 做了,记录当前矩形的左上角和右下角还有当前方向,每次 $O(1)$ 直接转移,时间复杂度 $O(n^4)$。然后发现空间不够,可以滚动掉一维,空间复杂度 $O(n^3)$。
参考代码:
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
#define drep(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
int n,m,md,x,y,ans,f1[103][103],f2[103][103],dp[102][2][102][102][4];
char s[103][103];
bool fl;
inline void add(int &x,int y){
x+=y;if(x>=md)x-=md;
}
signed main(){
scanf("%d%d%d%d%d",&n,&m,&md,&y,&x);
rep(i,1,n)scanf("%s",s[i]+1);
rep(i,1,n)rep(j,1,m){
f1[i][j]=f1[i][j-1]+(s[i][j]=='*');
f2[i][j]=f2[i-1][j]+(s[i][j]=='*');
}
rept(i,0,4)dp[x][0][x][y][i]=1;
drep(y1,y,1){
drep(x1,x,1)rep(x2,x,n)rep(y2,y,m)rept(i,0,4)dp[x1][fl^1][x2][y2][i]=0;
drep(x1,x,1){
rep(x2,x,n){
rep(y2,y,m){
if(x2<n&&s[x2+1][y1]=='+'){
add(dp[x1][fl][x2+1][y2][0],dp[x1][fl][x2][y2][0]);
if(f1[x2+1][y1-1]==f1[x2+1][y2])add(dp[x1][fl][x2+1][y2][1],dp[x1][fl][x2][y2][0]);
}
if(y2<m&&s[x2][y2+1]=='+'){
add(dp[x1][fl][x2][y2+1][1],dp[x1][fl][x2][y2][1]);
if(f2[x1-1][y2+1]==f2[x2][y2+1])add(dp[x1][fl][x2][y2+1][2],dp[x1][fl][x2][y2][1]);
}
if(x1>1&&s[x1-1][y2]=='+'){
add(dp[x1-1][fl][x2][y2][2],dp[x1][fl][x2][y2][2]);
if(f1[x1-1][y1-1]==f1[x1-1][y2])add(dp[x1-1][fl][x2][y2][3],dp[x1][fl][x2][y2][2]);
}
if(y1>1&&s[x1][y1-1]=='+'){
add(dp[x1][fl^1][x2][y2][3],dp[x1][fl][x2][y2][3]);
if(f2[x1-1][y1-1]==f2[x2][y1-1])add(dp[x1][fl^1][x2][y2][0],dp[x1][fl][x2][y2][3]);
}
}
}
}
fl^=1;
}
rep(x1,1,n)rep(y2,1,m)add(ans,dp[x1][fl^1][n][y2][0]);
cout<<ans;
return 0;
}