基础知识:
queue队列的相关用法:先进先出(FIFO)
入队push() //即插入元素
出队pop() //即删除元素
front() //读取队首元素
back() //读取队尾元素
empty() //判断队列是否为空
size() //读取队列当前元素的个数
————————————————————————————————————————————————————————————————————————
一个人在n点,牛在k点,这个人有两种移动方式,+1,-1,*2;移一下就是一分钟。问最短时间?
典型的BFS,,一个发散式的搜索,对于每个点,找出其可以到达的下三个点,第一个到达k的,此为最快,输出所需时间即可。
#include<iostream> #include<cstring> #include<cstdio> #include<queue> const int maxn = 1e5 +10; int st[maxn]; bool vis[maxn]; using namespace std; queue<int>q; void first() { memset(st,0,sizeof(st)); memset(vis,false,sizeof(vis)); while(!q.empty()) { q.pop(); //记得初始化队列 } } int bfs(int n,int k) { int head,next; q.push(n); st[n]=0; vis[n]=true; while(!q.empty()) { head=q.front(); //取头 q.pop(); //去头 for(int i = 0; i<3 ; i++) { if(i==0) next=head-1; else if(i==1) next=head+1; else next=head*2; if(next<0||next>maxn) continue; if(!vis[next]) { q.push(next); //压入 st[next]=st[head]+1; vis[next]=true; } if(next==k) return st[next]; } } } int main() { int n , k ; while(cin>>n>>k){ first(); if(n>=k) { cout<<n-k<<endl; continue; } else cout<<bfs(n,k)<<endl; } }
n-皇后问题,DFS解法:
题中已经说的很清楚了,什么叫n皇后。对于一个棋子的摆放,我们应该保证这个棋子所在位置的一列,斜方向和反斜方向没有棋子。怎么判断呢,我们引入bool类型的row[]数组,row[i]=true,表明这一列已经放过棋子了,false表示没有。这个row就解决了列问题,那么斜方向问题怎么解决呢?我们观察一下,题中规定的斜方向,都是符合y=x+b/y=-x+b方程的。b=y-x/b=x+y。同一个b,就是同一条斜线了。我的代码中规定了d[]表示斜方向,ud[]表示反斜方向。比如!d[u+i],表示x=u,y=i这条斜线上没有棋子,这个b=u+i之前没有出现过。前面就说过,由于斜率一样,所以只要保证这个b不一样,所在斜线就不一样。同理,反斜方向的也是一样,用b=y-x来判断,但是如果y-x<0的话,需要+n偏移一下,统一用ud[i-u+n]。
if(!row[i]&&!d[u+i]&&!ud[i-u+n])表示,对于x=u,y=i这个点,只要同一列,两个斜线处均没有棋子,就符合要求,放上一枚棋子。
其他的就是dfs的基本操作了,还原啦什么的...
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N = 27; char m[N][N]; bool row[N],d[N],ud[N]; int n; void first() { for(int i = 0 ; i< n ; i ++) for(int j = 0 ; j < n ; j ++) m[i][j]='.'; memset(row,false,sizeof(row)); memset(d,false,sizeof(d)); memset(ud,false,sizeof(ud)); } void dfs(int u) { //cout<<u<<endl; if(u==n) { for(int i = 0 ; i < n ; i ++) { for(int j = 0 ; j < n ;j ++) cout<<m[i][j]; cout<<endl; } cout<<endl; return ; } for(int i = 0 ; i < n ; i++) { if(!row[i]&&!d[u+i]&&!ud[i-u+n]) { m[u][i]='Q'; row[i]=d[u+i]=ud[i-u+n]=true; dfs(u+1); row[i]=d[u+i]=ud[i-u+n]=false; m[u][i]='.'; } } } int main() { while(cin>>n) { first(); dfs(0); } }
另一种比较原始的搜索方式,比较慢:记录皇后数目,如果==n的话,就输出;
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N = 27; char m[N][N]; bool row[N],d[N],ud[N],urow[N]; int n; void dfs(int x,int y,int s) { //cout<<x<<" "<<y<<" "<<s<<endl; if(y==n) { x++; y=0; } if(x==n) { if(s==n) //这行不能放入if(x==n)里面,因为不管数目达没达到,都要return 掉 { for(int i=0;i<n;i++) puts(m[i]); puts(""); } return ; } if(!row[x]&&!urow[y]&&!d[x+y]&&!ud[y-x+n]) { m[x][y]='Q'; row[x]=urow[y]=d[x+y]=ud[y-x+n]=true; dfs(x,y+1,s+1); m[x][y]='.'; row[x]=urow[y]=d[x+y]=ud[y-x+n]=false; } dfs(x,y+1,s); } int main() { //int n; cin>>n; for(int i=0;i<n;i++) for(int j=0;j<n;j++) m[i][j]='.'; dfs(0,0,0); }
ACWING 844.走迷宫:https://www.acwing.com/problem/content/846/
走迷宫,从左上角走到右下角,0可走,1不可走,问最少需要几步。
BFS解法: 通过g[][]记录地图,d[][]初始化为-1用来保证一个点只能走一次以及记录每个点到达终点的距离。
注意:使用以下语句来实现队列记录(x,y);
#include<queue> typedef pair<int,int>pp;
queue<int>q;
q.push({1,2});
pp t=q.front;
那么:t.first=1,t.second=2
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; typedef pair<int,int>P; //!!! const int maxn = 105; int n,m; int g[maxn][maxn]; int d[maxn][maxn]; //每个点到起点的距离 //没有走过 int dx[]={0,0,-1,1}; int dy[]={1,-1,0,0}; int bfs() { queue<P>q; memset(d,-1,sizeof(d)); d[0][0]=0; q.push({0,0}); while(!q.empty()) { P t = q.front(); q.pop(); for(int i=0;i<4;i++) { int x=dx[i]+t.first; int y=dy[i]+t.second; if(x>=0&&y>=0&&x<n&&y<m&&g[x][y]==0&&d[x][y]==-1) { d[x][y]=d[t.first][t.second]+1; q.push({x,y}); } } } return d[n-1][m-1]; } int main() { cin>>n>>m; for(int i=0;i<n;i++) for(int j=0;j<m;j++) cin>>g[i][j]; cout<<bfs()<<endl; return 0; }
跟这个题基本一模一样的:POJ3984:http://poj.org/status
不同的是这个是要输出路径。我的做法是,pair一个ing[x][y],ing[x][y].first,ing[x][y].second记录x,y的前一个点。因为是逆序,所以又存进一个结构体里,再逆序输出,才变成正序了。
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; typedef pair<int,int>P; //!!! const int maxn = 105; P ing[maxn][maxn]; int n,m; int g[maxn][maxn]; int d[maxn][maxn]; //每个点到起点的距离 //没有走过 int dx[]={0,0,-1,1}; int dy[]={1,-1,0,0}; struct node { int x,y; }st[maxn]; void bfs() { queue<P>q; memset(d,-1,sizeof(d)); d[0][0]=0; q.push({0,0}); while(!q.empty()) { P t = q.front(); q.pop(); for(int i=0;i<4;i++) { int x=dx[i]+t.first; int y=dy[i]+t.second; if(x>=0&&y>=0&&x<5&&y<5&&g[x][y]==0&&d[x][y]==-1) { d[x][y]=d[t.first][t.second]+1; ing[x][y].first=t.first; ing[x][y].second=t.second; q.push({x,y}); } } } return ; } int main() { //cin>>n>>m; for(int i=0;i<5;i++) for(int j=0;j<5;j++) cin>>g[i][j]; bfs(); int x=4,y=4; int tot=0; while(!(x==0&&y==0)) { st[tot].x=x; st[tot].y=y; tot++; int tx=x,ty=y; x=ing[tx][ty].first; y=ing[tx][ty].second; } cout<<"(0, 0)"<<endl; for(int i=tot-1;i>=0;i--) printf("(%d, %d) ",st[i].x,st[i].y); return 0; }
经典油田问题:POJ 2386 http://poj.org/problem?id=2386
DFS,碰到W,就变成‘.’,再看八个方向,如果有W,就DFS。最后DFS的次数就是答案。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn = 200; char s[maxn][maxn]; int n,m; void dfs(int x,int y) { if(x<0||y<0||x>n||y>m) return ; s[x][y]='.'; for(int i=-1;i<=1;i++) { for(int j=-1;j<=1;j++) { int xx=x+i; int yy=y+j; if(xx>=0&&yy>=0&&xx<n&&yy<m&&s[xx][yy]=='W') { dfs(xx,yy); } } } } int main() { cin>>n>>m; for(int i=0;i<n;i++) for(int j=0;j<m;j++) cin>>s[i][j]; int ans=0; for(int i=0;i<n;i++) for(int j=0;j<m;j++) { if(s[i][j]=='W') { dfs(i,j); ans++; } } cout<<ans<<endl; }