[JZOJ3106]锻炼
题目大意:
给你一个$n\times m(n,m\leq 50)$的网格图,其中有一个四连通的障碍物。给定起点和终点,每次你可以走到和当前位置八连通的一个方格内,问绕障碍物一圈最短要走几格?
思路:
BFS求一下最短路,然后当一个点被访问两次时,把两次的最短路加起来即可。
然而这样显然是错的,因为并不能保证两次的路径刚好把障碍物包住,因此我们还需知道每条路径是从障碍物哪边绕过来的。
一种方法是使用扫描线,当两条路径会合时,从障碍物内一点发射一条扫描线,如果和最短路橡胶的次数时奇数,那么肯定是将障碍物包住了。
这样据说已经能过了,不过还有一种更妙的方法。
可以在BFS最短路时,同时记录这条最短路与扫描线相交的次数的奇偶性。
具体方法是,每次最短路往外扩展一格时,判断一下本次扩展是否经过了扫描线。
最后两条最短路会合时,只需要判断一下两个最短路经过次数加起来是否为奇数,如果是,则说明将障碍物包起来了,更新答案。
1 #include<queue> 2 #include<cstdio> 3 #include<cctype> 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 char getblock() { 12 register char ch; 13 do { 14 ch=getchar(); 15 } while(ch!='.'&&ch!='X'&&ch!='*'); 16 return ch; 17 } 18 typedef std::pair<int,int> Block; 19 const int N=50; 20 const int inf=0x7fffffff; 21 const int dx[]={-1,-1,-1,0,0,1,1,1},dy[]={-1,0,1,-1,1,-1,0,1}; 22 Block s; 23 bool f[N][N]; 24 int n,m,dis[N][N]; 25 std::queue<Block> q; 26 inline bool check(const Block &u) { 27 return ~u.first&&u.first<n&&~u.second&&u.second<m&&~dis[u.first][u.second]; 28 } 29 inline bool calc(const Block &u,const Block &v) { 30 return (u.first==s.first&&u.second>s.second&&v.first<s.first)||(v.first==s.first&&v.second>s.second&&u.first<s.first); 31 } 32 int main() { 33 n=getint(),m=getint(); 34 for(register int i=0;i<n;i++) { 35 for(register int j=0;j<m;j++) { 36 const char ch=getblock(); 37 if(ch=='*') q.push((Block){i,j}); 38 if(ch=='X') { 39 s=(Block){i,j}; 40 dis[i][j]=-1; 41 } 42 if(ch=='.') dis[i][j]=inf; 43 } 44 } 45 int ans=inf; 46 while(!q.empty()) { 47 const Block u=q.front(); 48 q.pop(); 49 for(register int i=0;i<8;i++) { 50 const Block v=(Block){u.first+dx[i],u.second+dy[i]}; 51 if(!check(v)) continue; 52 if(dis[v.first][v.second]==inf) { 53 q.push(v); 54 dis[v.first][v.second]=dis[u.first][u.second]+1; 55 f[v.first][v.second]=f[u.first][u.second]^calc(u,v); 56 } else { 57 if(f[u.first][u.second]^calc(u,v)^f[v.first][v.second]) { 58 ans=std::min(ans,dis[u.first][u.second]+dis[v.first][v.second]+1); 59 } 60 } 61 } 62 } 63 printf("%d\n",ans); 64 return 0; 65 }