本文全部摘抄自下述网站,只添加本人的一点理解
参考网站:网易博客
/**
序号:num_5
作者:MrZhang
日期:2016-5-24
题目名称: Slash Maze(斜线迷宫)
题目来源:
uva —— 705 —— Slash Maze
网址:
英文题目:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=19488
--------------------------------------
数据样例:
input:
6 4
\//\\/
\///\/
//\\/\
\/\///
3 3
///
\//
\\\
0 0
output
Maze #1:
2 Cycles; the longest has length 16.
Maze #2:
There are no cycles.
*/
#include <iostream>
#include <cstring>
#define MAXN 160
using namespace std;
int G[MAXN][MAXN],n,m,sum,maxlen;//!sum :当前环的长度 maxlen:最长环的长度
//! 0 1 2 3 4 5 6 7
//! 上 下 左 右 左上 右下 左下 右上
const int _move[8][2]={{-1,0},{1,0},{0,-1},{0,1},{-1,-1},{1,1},{1,-1},{-1,1}};
bool vis[MAXN][MAXN],flag;
void dfs(int x,int y)
{
vis[x][y]=true;
int px,py;
sum++;
if(x==0 || y==0 || x==2*n-1 || y==2*m-1) {
flag=true;
}
for(int i=0;i<8;++i)
{
px=x+_move[i][0];
py=y+_move[i][1];
if(px>=0 && px<2*n && py>=0 && py<2*m && G[px][py]!=1 && !vis[px][py])
{
if(i<4) dfs(px,py);
else
{
if(i==4 && G[x-1][y]==1 && G[x][y-1]==1)//左上
{
if((G[x-1][y-2]==1 && G[x][y+1]==1 && (x&1) && !(y&1)) ||
(G[x-2][y-1]==1 && G[x+1][y]==1 && !(x&1) && (y&1)))
{
dfs(px,py);
}
}
if(i==5 && G[x][y+1]==1 && G[x+1][y]==1)//右下
{
if((G[x+1][y+2]==1 && G[x][y-1]==1 && !(x&1) && (y&1)) ||
(G[x-1][y]==1 && G[x+2][y+1]==1 && (x&1) && !(y&1)))
{
dfs(px,py);
}
}
if(i==6 && G[x][y-1]==1 && G[x+1][y]==1)//左下
{
if((G[x-1][y]==1 && G[x+2][y-1]==1 && (x&1) && (y&1)) ||
(G[x][y+1]==1 && G[x+1][y-2]==1 && !(x&1) && !(y&1)))
{
dfs(px,py);
}
}
if(i==7 && G[x-1][y]==1 && G[x][y+1]==1)//右上
{
if((G[x][y-1]==1 && G[x-1][y+2]==1 && (x&1) && (y&1)) ||
(G[x+1][y]==1 && G[x-2][y+1]==1 && !(x&1) && !(y&1)))
{
dfs(px,py);
}
}
}
}
}
}
void solve()
{
sum=0; maxlen=0;
int cnt=0;
for(int i=0;i<2*n;++i)
{
for(int j=0;j<2*m;++j)
{
if(G[i][j]==0 && !vis[i][j])
{
sum=0;
flag=false;//!进入图开始遍历,怎样才算是一个环呢?只要在遍历的过程中没有
dfs(i,j);
if(!flag)
{
if(maxlen<sum) maxlen=sum;
sum=0;
cnt++;
}
}
}
}
if(cnt) cout<<cnt<<" Cycles; the longest has length "<<maxlen<<"."<<endl;
else cout<<"There are no cycles."<<endl;
}
void read_input()
{
char s[80];
memset(G,0,sizeof(G));
memset(vis,false,sizeof(vis));
for(int i=0;i<2*n;i+=2)
{
cin>>s;
for(int j=0;j<m;j++)
{
if(s[j]=='\\') G[i][j*2]=G[i+1][2*j+1]=1;
else G[i][2*j+1]=G[i+1][2*j]=1;
}
}
}
//void out_put()
//{
// for(int i=0;i<2*n;++i)
// {
// for(int j=0;j<2*m;++j)
// {
// if(G[i][j]) cout<<"1";
// else cout<<"0";
// }
// cout<<endl;
// }
// cout<<endl;
//}
int main()
{
int T=1;
while(cin>>m>>n)
{
if(m+n==0) break;
cout<<"Maze #"<<T++<<":"<<endl;
read_input();
solve();
cout<<endl;
//out_put();
}
return 0;
}
/**
总结:
题型:图的遍历
原理:DFS
技巧:使用四个二进制数表示一个斜线
针对此题目的理解:
1.首先是环的概念,怎样才算是一个环呢?
见英文,注意两句:
(1)paths in the maze cannot branch.
(2)the whole maze only contains cyclic paths and paths entering somewhere and leaving somewhere else
解释:
由上可知:
在此迷宫中不存在分支,即'Y'型的路。
所以 只存在两种路:a.单方向的环 b.路的两端均在迷宫的边上(注意到了边就不可能再回来了)
*/
2. flag标志的作用
如下图:黄色代表边界,灰色代表路径,红点代表开始遍历的点。
所以,下面的路即上面的'b'型路径。
序号(1)(2)代表遍历的顺序。
过程分析:
首先从红点沿着(1)方向遍历,在遍历过程中不断将visited设置为1。到达迷宫边界后将flag设置为true,因为不能再走了,所以不断回溯至红色原点处。
然后朝其他可走的方向遍历(此处即(2)方向了),同样设置visited,
想想沿着方向2会发生什么情况?成环了!!!可这种环是无效的。
但仍然会正常的遍历完,目的是将这条假环的visited全设置成1,以免以后再遍历。
遍历完后,这次dfs全部结束,那么问题来了,程序会将这个环计算在内吗?(即cnt 会 ++吗?)
不会,因为在这次遍历中,上面的(1)已经导致了flag设置为true,而
if(!flag)
{
if(maxlen<sum) maxlen=sum;
sum=0;
cnt++;
}
所以,该次遍历不会计数在内。
故而,真是由于flag的存在才避免了这种假环的计数。
图1
3.sum,为什么在某部分方向的遍历失败后,回溯到原点后没有将sum恢复原值?
请看上面的a.b.c三种路,因为只有bc型路都到过边界,所以其flag都设置为true了,这些错误的路的sum不会被采纳。
只有a型路,这种正确的单方向的路的sum才会被采纳,所以不用担心sum失误。
其实,即使恢复sum 也是可以的。只有稍有点麻烦罢了。