今天做了几道kuanbin专题,今天做的算是搜索入门题吧,不过自己确实做得够呛(黑脸)
迷宫问题 POJ - 3984
这道题就是bfs的模板题了,题意很简单,就找到从左上角到右下角的最短路线,这又让我想到前天CometOJ的最后一题I,那时候做的时候以为是同类型的,所以想的有些复杂,最后自己才发现有套路,那道题是障碍随机,即只要找到最短路就好了。
这里用到了结构体,用来存放输入的坐标还有值,其中值1代表障碍,0代表可以走,用了 l 数组记录下一步方向,以路径的长度作为下标,因为一条路径对应每一段长度的是唯一的。其中走路的方向我用两个数组来存储,这样就可以利用循环来尝试。用a数组记录是否走过。用judge函数判断是否可以走。用bfs函数解题,这里用到了队列,这个算是一个模板,先将第一个数据初始化入栈,然后再不断地更新队列,入栈出栈,入栈后用 l 数组记录方向。
PS:悄咪咪地说一句,这道题的测试数据只有示例那一组,有些人就直接输入输出就Accepted,而且内存超小速度快。
//bfs
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
//# define LOCAL
using namespace std;
const int maxn = 5;
int map[maxn][maxn];//图
bool a[maxn][maxn];//记录是否走过
int dx[4]={0,-1,0,1};
int dy[4]={-1,0,1,0};//走路方向
struct node{
int x,y,s;
short l[30];
};
bool judge(int i,int j){
if(i<0 || i>=maxn || j<0 || j>=maxn) return true;//边界
if(a[i][j]==true) return true;//走过
if(map[i][j] == 1) return true;//障碍
return false;
}
node bfs(){
queue<node> q;
node cur,next;
cur.x = 0,cur.y = 0,cur.s = 0;
a[cur.x][cur.y] = true;
q.push(cur);
while(!q.empty()){
cur = q.front();
q.pop();
if(cur.x == maxn-1 && cur.y == maxn-1)
return cur;
for(int i=0;i<maxn-1;i++){
int nx = cur.x + dx[i];
int ny = cur.y + dy[i];//尝试向这个方向走
if(judge(nx,ny))
continue;
next = cur;//往下走
next.x = nx;
next.y = ny;
next.s = cur.s + 1;
next.l[cur.s] = i;
q.push(next);
a[next.x][next.y] = true;
}
}
return cur;
}
inline int read(){
char ch=getchar();int f=1,x=0;
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int main(){
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
for(int i=0;i<maxn;i++)
for(int j=0;j<maxn;j++)
map[i][j] = read();
// scanf("%d",&map[i][j]);
memset(a,0,sizeof(a));
node ans = bfs();
int x=0,y=0;
for(int i=0;i<=ans.s;i++){
printf("(%d, %d)\n",x,y);
x += dx[ans.l[i]];
y += dy[ans.l[i]];
}
return 0;
}
Ordering Tasks UVA - 10305
这道是紫书上的一道,任务安排,这里我用了两种方法,紫书使用数组,这里还写了一种利用队列。
这道题也算是拓扑排序的一道模板题,其中讲到拓扑排序,需要了解下概念
- 拓扑排序
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。 通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。
其中我们需要知道的是,如果图中存在有向环,则不存在拓扑排序,反之则存在。不包含有向环的有向图成为有向无环图(DAG)。
紫书用到的技巧是,利用c数组,c[u]=0表示从来没有访问过(从来没有调用过dfs(u));c[u]=1表示已经访问过,并且还递归访问它所有子孙(即dfs()曾被调用过,并已返回);c[u]=-1表示正在访问(即递归调用dfs(u)正在栈帧中,尚未返回)。
这里讲一下队列实现的方法:
其中deg数组存储的是结点的入度,其中入度是指以该结点为终点的有向边的条数。
拓扑排序的过程思想很简单,我们只需要不断地选择图中入度为0的结点,然后把x连向的点的入度减1。
代码中,用G数组存储是否已排序,topo存储排序好的序列。这里需要注意的是题目要求是输出的是从1开始的,所以我们最好在存入数据的时候就从1开始,省去麻烦。
#include<cstdio>
#include<cstring>
#include<queue>
# define LOCAL
using namespace std;
const int maxn = 105;
int G[maxn][maxn];
int c[maxn],topo[maxn];
int m,n,t;
bool dfs(int u){
c[u] = -1;//标志正在访问
for(int v=1;v<=n;v++){
if(G[u][v]){
if(c[v] < 0)
return false;
else if(!c[v] && !dfs(v))
return false;
}
}
c[u] = 1;//标记已经访问过了
topo[--t] = u;
return true;
}
bool toposort(){
t = n;
memset(c,0,sizeof(c));
for(int u=1;u<=n;u++)
if(!c[u])
if(!dfs(u))
return false;
return true;
}
int deg[maxn];
void Sort(){//拓扑排序
queue<int> q;
int cnt = 0;
for(int i=1;i<=n;i++)
if(deg[i]==0)
q.push(i);
while(!q.empty()){
int u = q.front();
q.pop();
topo[cnt++] = u;
for(int v=1;v<=n;v++){
if(G[u][v])
if(--deg[v] == 0)
q.push(v);
}
}
for(int i=0;i<cnt;i++)
printf("%d ",topo[i]);
printf("\n");
}
int main(){
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
while(scanf("%d%d",&n,&m)==2 && (m||n)){
memset(G,0,sizeof(G));
memset(deg,0,sizeof(deg));
for(int i=0;i<m;i++){
int u,v;
scanf("%d%d",&u,&v);
G[u][v] = 1;
deg[v] ++;
}
/* if(toposort()){
for(int i=0;i<n;i++)
printf("%d ",topo[i]);
printf("\n");
}*/
Sort();
}
return 0;
}
Dungeon Master POJ - 2251
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstdlib>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<ctime>
#include<vector>
# define LOCAL
using namespace std;
const int maxn = 35;
char mp[maxn][maxn][maxn];//map
bool vis[maxn][maxn][maxn];//judge
int sl,sx,sy,el,ex,ey; //start and end
int dic[6][3]={{-1,0,0},{1,0,0},{0,-1,0},{0,1,0},{0,0,-1},{0,0,1}}; //direction
int l,r,c;
priority_queue<node> q; //定义优先队列
struct node{
int l,x,y;
int step;e
friend bool operator < (node a,node b){
return a.step > b.step; //优先队列,小的先访问
}
};
bool judge(int i,int j,int k){ //判断
if(i<0 || j<0 || k<0 || i>=l || j>=r || k>=c) return false;
if(mp[i][j][k]=='#') return false;
if(vis[i][j][k]) return false;
return true;
}
void bfs(){
node p;
p.l = sl, p.x = sx, p.y = sy;//起点
p.step = 0;
vis[p.l][p.x][p.y] = true;
q.push(p); //入栈
while(!q.empty()){
node cur = q.top();
q.pop();
if(cur.l==el && cur.x==ex && cur.y==ey){
printf("Escaped in %d minute(s).\n",cur.step);
return;
}//到达终点
for(int i=0;i<6;i++){
int nl = cur.l + dic[i][0];
int nx = cur.x + dic[i][1];
int ny = cur.y + dic[i][2]; //尝试各个方向
if(judge(nl,nx,ny)){
node next;
next.l = nl,next.x = nx,next.y = ny;
next.step = cur.step + 1;
vis[next.l][next.x][next.y] = true;
q.push(next);
}
}
}
printf("Trapped!\n");
}
int main(){
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
while(scanf("%d%d%d",&l,&r,&c)==3 && ( l || r || c )){
for(int i=0;i<l;i++){
for(int j=0;j<r;j++){
// cin >> mp[i][j];
scanf("%s",mp[i][j]);
// cout << mp[i][j] << "\n";
for(int k=0;k<c;k++){
if(mp[i][j][k] == 'S'){
sl = i, sx = j, sy = k;//找到起点
}else if(mp[i][j][k] == 'E'){
el = i, ex = j, ey = k;
}//找到终点
}
}
// getchar();
}
memset(vis,0,sizeof(vis));
bfs();
}
return 0;
}
这道题其实和第一道迷宫问题很像,只是方向从2维变成3维,我们只需要在方向上做改动,然后再多维护一个变量就可以了。这里用到了优先队列,因为路径可能有很多条,我们需要维护路径最短的。这里把判断条件还是放在函数里,需要用的时候调用,这样也使得代码比较清晰,便于找到BUG,不然可能会有下面这样的情况,debug了很久才发现某个条件写错了。