= ,= 妈蛋,拓扑排序和欧拉回路先放一放,实在有点力不从心。先继续学习暴力破解法。在那之前,把八连块和走迷宫先记录一下。
八连块,此名字十分坑爹,其实只要是连着的黑色区域都叫做一个八连块。计算一个图中黑色八连快的个数,
分析:利用DFS的思想来从0,0起点遍历整个图,遍历的过程中先是寻找到一个没有被访问过的黑色点,然后对这个点开始分散地递归地分析其周围的连块,直到遇到边界。此时也使得这个区域的黑色点比标记过。
下面是我的垃圾代码。
char s[1000]; int map[1000][1000][2]; //最后两个数值[0] 是存储的黑白0是白色 1是黑色;[1] 是是否被访问过0是没访问过 1是访问过 //这是用了调用栈的方法来实现dfs的 ,但是这样做有可能造成溢出 void dfs(int x,int y) { if(map[x][y][0]==0||map[x][y][1]==1) return;//如果是白色或者被访问过就返回 map[x][y][1]=1;//自身被访问过 //否则开始递归访问周围的 dfs(x-1,y+1); dfs(x,y+1); dfs(x+1,y+1); dfs(x-1,y); dfs(x+1,y); dfs(x-1,y-1); dfs(x,y-1); dfs(x+1,y-1); } int main() { int n,cot=0; cin>>n; memset(map,0,sizeof(map)); //多维数组也可以这样清零 for(int i=1;i<=n;i++) { cin>>s; for(int j=1;j<=n;j++) map[i][j][0]=s[j-1]-'0'; //此处用到了一种创造白色边界的方法来便于控制出界 } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(map[i][j][0]==1&&map[i][j][1]==0) { cot++;//每次读到一个黑色点即可认为读到了一个黑色八连块 dfs(i,j);//开始在这个黑点周围释放毒气~ } } } cout<<cot<<endl; }
为了避免调用栈的溢出,不放选择用显式的栈来处理
stack<int> xs,ys; void dfs(int x,int y) { xs.push(x);ys.push(y);//首先将选中的黑色点压入栈底 while(!xs.empty()) { int xt=xs.top(); int yt=ys.top(); if(map[xt][yt][0]==0||map[xt][yt][1]==1) { xs.pop();ys.pop(); continue;} map[xt][yt][1]=1;//标志被访问过 <span style="white-space:pre"> </span>//开始讲周围的8点压入栈 准备进行处理,每次处理时又会进行压入,所以这种方法其实还是效率蛮低的 <span style="white-space:pre"> </span>//改良的话不放考虑一下xt,yt的范围 xs.push(xt-1);ys.push(yt-1); xs.push(xt-1);ys.push(yt); xs.push(xt-1);ys.push(yt+1); xs.push(xt);ys.push(yt-1); xs.push(xt);ys.push(yt+1); xs.push(xt+1);ys.push(yt-1); xs.push(xt+1);ys.push(yt); xs.push(xt+1);ys.push(yt+1); } }
这种时候发现递归其实并不一定适用于各种情况
下面研究一个最短路问题(貌似是叫这个名字)
走迷宫,寻找最短路线(其中一种就行,其实也只能找出来一种)
分析时,考虑怎么样才能得到最短路线。
1.把地图上每个点距离起点的最短距离写出来
2.把每个点的上一个来源找到(指的是实现起点到此点最短距离的路线)
下面是代码
其实我对BFS和DFS的理解还是不透彻,只是知道BFS用队列,DFS用栈(递归)
#include <stdio.h> #include <iostream> #include <queue> #define M 1000 using namespace std; int maze[M][M];//存储地图 1是空地 0是陷阱 char s[M];//存储临时输入变量 char name[]={' ','U','D','L','R'};//1 2 3 4 分别对应的走向 int n,m;//地图宽度n 地图高度m int dist[M][M],father[M][M];//dist 用来记录距离 father用来记录父节点 bool isvit[M][M]={false};//记录是否被走过,默认是false 没走过 int last_dir[M][M]; //下面两个数组非常简洁的解决了如何处理防线转换的判断问题 int dx[5]={0,0,0,-1,1}; int dy[5]={0,-1,1,0,0}; char path[M*M]; //bfs 用来遍历整个地图 同时进行标记每个点到起点的距离 //参数是起点的位置 0,0 void bfs(int x,int y) { queue<int> q; //用来存储即将要处理的节点,由于是bfs则用队列处理 int u =x+n*y;//记下起点编号 isvit[x][y]=true;//走过了 father[x][y]=u;//起点的父节点就是起点本身 dist[x][y]=0;//自己到自己是0 //开始遍历队列中的节点 q.push(u);//第一个要处理的就是起点 while(!q.empty()) { u=q.front();//记下即将要处理的节点编号 q.pop();//表示已经开始接受处理 int ux=u%n,uy=u/n; for(int i=1;i<=4;i++) { int tx=ux+dx[i]; int ty=uy+dy[i]; //tx ty 不越界且是空地 且没有被访问过 if(tx>=0&&tx<n&&ty>=0&&ty<m&&maze[tx][ty]&&!isvit[tx][ty]) { dist[tx][ty]=dist[ux][uy]+1;//起点到此扩展节点的距离就是起点到其父节点最短距离+1 father[tx][ty]=u;//记录父节点 isvit[tx][ty]=true;//标志被访问过 last_dir[tx][ty]=i; int v=tx+ty*n; q.push(v); } } } } //打印路线图 递归调用 向上一个节点靠近 反向输出 void print_path(int x,int y) { int fa=father[x][y]; if(x==0&&y==0) return; //因为路线过程中只有起点的父节点是0,0需要直接退回 int tx=fa%n; int ty=fa/n; print_path(tx,ty);//起点的父节点是0,0 所以会退回 其他的点都有父节点 cout<<name[last_dir[x][y]]; } //不用递归来做防止栈越界 void print_path2(int x,int y) { int i=0; while(!(x==0&&y==0)) { int fa=father[x][y]; int tx=fa%n; int ty=fa/n; path[i++]=name[last_dir[x][y]]; x=tx;y=ty;//此处相当于更新了队首元素 } //此时i比下标上限大1 while(i>0) { cout<<path[--i]; } } int main() { freopen("in47.txt","r",stdin); freopen("out47.txt","w",stdout); cin>>n>>m; for(int i=0;i<m;i++) { cin>>s; for(int j=0;j<n;j++) maze[j][i]=s[j]-'0'; } bfs(0,0); for(int i=0;i<m;i++) { for(int j=0;j<n;j++) cout<<dist[j][i]<<" "; cout<<endl; } cout<<endl; cout<<endl; cout<<endl; for(int i=0;i<m;i++) { for(int j=0;j<n;j++) cout<<father[j][i]<<" "; cout<<endl; } cout<<endl; cout<<endl; cout<<endl; print_path(4,0); cout<<endl; print_path2(4,0); return 0; }