今天做了幾道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了很久才發現某個條件寫錯了。