DFS(深搜)
之前写过三篇关于dfs的
练习总结:
基础算法--递归搜索DFS练习总结(上)-CSDN博客
基础算法--递归搜索DFS练习总结(中)-CSDN博客
基础算法--递归搜索DFS练习总结(下)-CSDN博客
以下题目均为
补充练习:
P1460 [USACO2.1] 健康的荷斯坦奶牛 Healthy Holsteins
第一种暴力搜索dfs(超时未AC 90 points):
#include#include using namespace std; int n,m,a[30],b[20][30],c[30],ans; bool flag[20],key; bool check(){ for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++) if(flag[j]) c[i]+=b[j][i]; if(a[i]>c[i]) return false; } return true; } void dfs(int x,int cnt){ if(x==cnt+1){ if(check()) {key=true;ans=cnt;} memset(c,0,sizeof c); return ; } for(int i=x;i<=m;i++){ if(!flag[i]){ flag[i]=true; dfs(x+1,cnt); if(key) return ; flag[i]=false; } } } int main(){ cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; cin>>m; for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) cin>>b[i][j]; for(int i=1;i<=m;i++){ memset(flag,0,sizeof flag); memset(c,0,sizeof c); dfs(1,i); if(key){ cout<
第二种暴力搜索dfs(AC):
#includeusing namespace std; int n,m,a[30],b[20][30],c[30],ans=1e9,res[30]; //数组c:每次搜索选的饲料编号,数组res:存储解 //判断每次选的那些饲料中的维生素之和是不是都大于等于牛需要的量 bool check(int x){ for(int i=1;i<=n;i++){ int sum=0; for(int j=1;j<=x;j++) sum+=b[c[j]][i]; if(summ){//当前位置超过边界 if(check(curCnt)){//必须使牛够吃 if(curCnt>n; for(int i=1;i<=n;i++) cin>>a[i]; cin>>m; for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) cin>>b[i][j]; dfs(1,0); cout<
P1451 求细胞数量
dfs(AC):
思路:不为0的地方就是细胞,然后dfs搜连通块,把搜到的都归0,保证不重复
#includeusing namespace std; int n,m,ans; char a[105][105]; int dx[4]={-1,0,1,0}; int dy[4]={0,1,0,-1}; void dfs(int x,int y){ if(x<0||y<0||x>=n||y>=m) return ; a[x][y]='0'; for(int i=0;i<4;i++){ int xx=x+dx[i],yy=y+dy[i]; if(xx>=0&&xx =0&&yy >n>>m; for(int i=0;i >a[i]; for(int i=0;i P1331 海战
dfs(AC):
和细胞一样的思路,唯一需要注意的是对于斜着连着的情况要特殊输出
#includeusing namespace std; int n,m,ans; char a[1005][1005]; int dx[4]={-1,0,1,0}; int dy[4]={0,1,0,-1}; bool flag; void dfs(int x,int y){ if(flag) return ; if(x<0||y<0||x>=n||y>=m) return ; a[x][y]='.'; if(a[x-1][y+1]=='#'&&a[x-1][y]=='.'&&a[x][y+1]=='.') {flag=true; return ;} if(a[x+1][y+1]=='#'&&a[x+1][y]=='.'&&a[x][y+1]=='.') {flag=true; return ;} if(a[x+1][y-1]=='#'&&a[x+1][y]=='.'&&a[x][y-1]=='.') {flag=true; return ;} if(a[x-1][y-1]=='#'&&a[x-1][y]=='.'&&a[x][y-1]=='.') {flag=true; return ;} for(int i=0;i<4;i++){ int xx=x+dx[i],yy=y+dy[i]; if(xx>=0&&xx =0&&yy >n>>m; for(int i=0;i >a[i]; for(int i=0;i B3625 迷宫寻路
dfs(AC):
#includeusing namespace std; int n,m; char a[105][105]; int dx[4]={-1,0,1,0}; int dy[4]={0,1,0,-1}; void dfs(int x,int y){ if(a[x-1][y]=='#'){ if(a[x][y+1]=='#'){ if(a[x+1][y]=='#'){ if(a[x-1][y]=='#'){ return ; } } } } for(int i=0;i<4;i++){ int xx=x+dx[i],yy=y+dy[i]; if(xx>0&&xx<=n&&yy>0&&yy<=m&&a[xx][yy]=='.'){ a[xx][yy]='#'; dfs(xx,yy); } } } int main(){ cin>>n>>m; for(int i=0;i<=m+1;i++) a[0][i]='#',a[n+1][i]='#'; for(int i=1;i<=n;i++) a[i][0]='#',a[i][m+1]='#'; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>a[i][j]; dfs(1,1); if(a[n][m]=='#') cout<<"Yes"< bfs(AC):
#include#include #include using namespace std; typedef pair pii; int n,m,d[105][105]; char a[105][105]; int dx[4]={-1,0,1,0}; int dy[4]={0,1,0,-1}; int bfs(){ queue q; q.push({0,0}); memset(d,-1,sizeof d); d[0][0]=0; while(q.size()){ auto t=q.front(); q.pop(); for(int i=0;i<4;i++){ int x=t.first+dx[i],y=t.second+dy[i]; if(x>=0&&x =0&&y >n>>m; for(int i=0;i >a[i]; if(bfs()==-1) cout<<"No"< P1162 填涂颜色
思路:
利用dfs把原来的0变成1,并且标记数组记住原来的0,方便后续输出
然后图中剩下的1就是1,剩下的0就是2,输出即可
#includeusing namespace std; const int N=35; int n,a[N][N],flag[N][N]; int dx[4]={-1,0,1,0}; int dy[4]={0,1,0,-1}; void dfs(int x,int y){ a[x][y]=1; flag[x][y]=1; if(a[x-1][y]==1){ if(a[x][y+1]==1){ if(a[x+1][y]==1){ if(a[x][y-1]==1){ return ; } } } } for(int i=0;i<4;i++){ int xx=x+dx[i],yy=y+dy[i]; if(xx>0&&xx<=n&&yy>0&&yy<=n&&a[xx][yy]==0){ dfs(xx,yy); } } } int main(){ cin>>n; for(int i=0;i<=n+1;i++) a[0][i]=1,a[n+1][i]=1; for(int i=1;i<=n;i++) a[i][0]=-1,a[i][n+1]=-1; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>a[i][j]; for(int i=1;i<=n;i++){ if(a[1][i]==0) {flag[1][i]=1;dfs(1,i);} if(a[n][i]==0) {flag[n][i]=1;dfs(n,i);} } for(int i=1;i<=n-1;i++){ if(a[i][1]==0) {flag[i][1]=1;dfs(i,1);} if(a[i][n]==0) {flag[i][n]=1;dfs(i,n);} } for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(flag[i][j]) cout<<"0"<<" "; else{ if(a[i][j]) cout<<"1"<<" "; else cout<<"2"<<" "; } } cout< P1506 拯救oibh总部
和上面几题一样的思路dfs(AC):
#includeusing namespace std; const int N=505; int n,m,ans; char a[N][N]; int dx[4]={-1,0,1,0}; int dy[4]={0,1,0,-1}; void dfs(int x,int y){ a[x][y]='*'; if(a[x-1][y]=='*'&&x-1>=0&&x-1 =0&&y =0&&x =0&&y+1 =0&&x+1 =0&&y =0&&x =0&&y-1 =0&&xx =0&&yy >n>>m; for(int i=0;i >a[i]; for(int i=0;i P8662 [蓝桥杯 2018 省 AB] 全球变暖
这题非常重要的一点是:原来的岛屿经过海水淹没后(只要没完全被淹没),仍然认为他们是同一座岛屿!!!
模拟下图样例:
海平面上升后:
答案为:4(原来有8个岛屿,红色是没有被完全淹没的,那么被完全淹没的就有4个)
因此思路是:先dfs所有原来的岛屿,接着dfs没被完全淹没的岛屿,最后相减得结果(AC):
#includeusing namespace std; const int N=1005; char a[N][N],b[N][N]; int n,res,ans; int dx[4]={-1,0,1,0}; int dy[4]={0,1,0,-1}; void dfs_all(int x,int y){ b[x][y]='.'; if(b[x-1][y]=='.'&&x-1>=0&&x-1 =0&&y =0&&x =0&&y+1 =0&&x+1 =0&&y =0&&x =0&&y-1 =0&&x-1 =0&&y =0&&x =0&&y+1 =0&&x+1 =0&&y =0&&x =0&&y-1 >n; for(int i=0;i >a[i][j];b[i][j]=a[i][j];} for(int i=0;i BFS(广搜)
模拟队列模板:
typedef pairpii; int mapp[N][N]; int d[N][N];//每一个点到起点的距离 pii q[N*N];//手写队列 int bfs(){ int hh=0,tt=0; q[0]={0,0}; memset(d,-1,sizeof d);//距离初始化为-1表示没有走过 d[0][0]=0;//表示起点走过了 int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};//方向向量 while(hh <= tt){//队列不空 auto t=q[hh++];//取走队头元素 for(int i=0;i<4;i++){//枚举4个方向 int x=t.first+dx[i],y=t.second+dy[i];//更新方向 if(x>=0&&x =0&&y STL模板:
typedef pairpii; int mapp[N][N],d[N][N]; int bfs(){ queue q; q.push({0,0}); memset(d,-1,sizeof d); d[0][0]=0; int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1}; while(q.size()){ auto t=q.front(); q.pop(); for(int i=0;i<4;i++){ int x=t.first+dx[i],y=t.second+dy[i]; if(x>=0&&x =0&&y 打印路径模板:
typedef pairpii; int mapp[N][N],d[N][N]; pii preNode[N][N];//存储每个点的前一个点的坐标,方便追踪路径 int bfs(){ queue q; q.push({0,0}); memset(d,-1,sizeof d); d[0][0]=0; int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1}; while(q.size()){ auto t=q.front(); q.pop(); for(int i=0;i<4;i++){ int x=t.first+dx[i],y=t.second+dy[i]; if(x>=0&&x =0&&y path; //从终点遍历追踪 for(pii x={n-1,m-1};x!=make_pair(0,0);x=preNode[x.first][x.second]) path.push_back(x); path.push_back({0,0});//加入起点 reverse(path.begin(), path.end());//将路径逆序 for(auto x:path) cout<<"("< "; cout<<"终点"< bfs练习:
B3626 跳跃机器人
一维路径问题 bfs(AC):
#include#include #include using namespace std; const int N=1e6+5; int n,d[N]; int bfs(){ queue q; q.push({1}); memset(d,-1,sizeof d); d[1]=0; while(q.size()){ auto t=q.front(); q.pop(); if(t-1>=1&&t-1<=n&&d[t-1]==-1){ d[t-1]=d[t]+1; q.push({t-1}); } if(t+1>=1&&t+1<=n&&d[t+1]==-1){ d[t+1]=d[t]+1; q.push({t+1}); } if(2*t>=1&&2*t<=n&&d[2*t]==-1){ d[2*t]=d[t]+1; q.push({2*t}); } } return d[n]; } int main(){ cin>>n; cout< P2802 回家
二维路径问题+多种情况的判断 bfs(AC):
#include#include #include using namespace std; const int N=10; int n,m,xbegin,ybegin; struct point{ //结构体存储当前坐标状态 int px,py,pstep,php;//坐标,步数,血量 }; int a[N][N]; //地图 bool flag[N][N];//标记经过鼠标 int dx[4]={-1,0,1,0}; int dy[4]={0,1,0,-1}; int bfs(int x,int y){ queue q; //注意类型是结构体 q.push(point{x,y,0,6});//出发点入队 while(q.size()){ auto t=q.front(); //取队头元素 int x=t.px,y=t.py,step=t.pstep,hp=t.php;//初始化各变量 q.pop(); //出队 if(a[x][y]==3) return step;//走到家就结束 if(hp>1){ //hp<=1时直接死亡 for(int i=0;i<4;i++){ int xx=x+dx[i],yy=y+dy[i]; if(xx>=0&&xx =0&&yy >n>>m; for(int i=0;i >a[i][j]; if(a[i][j]==2) xbegin=i,ybegin=j; } } cout< P1135 奇怪的电梯
这题会卡纯的dfs,当然也可以搜索每一条路后就更新答案再继续搜索(未AC 64points):
#includeusing namespace std; int n,a,b,k[205],ans=1e6,flag[205]; //当前在第x楼,按了cnt次按钮 void dfs(int x,int cnt){ if(cnt>=ans) return ; if(x==b){ ans=min(ans,cnt); return ; } flag[x]=1; if(x+k[x]<=n&&flag[x+k[x]]==0){ //上楼 flag[x+k[x]]=1; dfs(x+k[x],cnt+1); flag[x+k[x]]=0; } if(x-k[x]>=1&&flag[x-k[x]]==0){ //下楼 flag[x-k[x]]=1; dfs(x-k[x],cnt+1); flag[x-k[x]]=0; } } int main(){ cin>>n>>a>>b; for(int i=1;i<=n;i++) cin>>k[i]; dfs(a,0); if(ans==1e6) {cout<<"-1"< bfs(AC):
#include#include using namespace std; const int N=205; int n,A,B,lift[N]; bool flag[N]; //标记数组作用是保证当前位置没有来过,因为如果回到原来经过的位置,那么就会一直重复 struct point{ int loc,ans;//位置,当前已经走的步数 }; int bfs(int a,int b){ queue q; q.push(point{a,0}); while(q.size()){ auto t=q.front(); int location=t.loc,step=t.ans; q.pop(); if(location==b) return step;//到达终点 if(location+lift[location]<=n){//可以向上 if(!flag[location+lift[location]]){//并且该位置没有来过 flag[location+lift[location]]=true; q.push(point{location+lift[location],step+1}); } } if(location-lift[location]>=1){//可以向下 if(!flag[location-lift[location]]){//并且该位置没有来过 flag[location-lift[location]]=true; q.push(point{location-lift[location],step+1}); } } } return -1; } int main(){ cin>>n>>A>>B; for(int i=1;i<=n;i++) cin>>lift[i]; cout< P1747 好奇怪的游戏
这题注意初始化flag标记数组,并且把数组开大一点就没问题了
bfs(AC):
#include#include #include using namespace std; const int N=50; int x1,y1,x2,y2; int dx[12]={-2,-2,-1,-2,1,-2,2,-1,2,1,2,2}; int dy[12]={-2,-1,-2,1,-2,2,-2,2,-1,2,1,2}; bool flag[N][N]; struct point{ int px,py,pstep;//坐标,步数 }; int bfs(int a,int b){ queue q; q.push(point{a,b,0}); memset(flag,0,sizeof flag); flag[a][b]=true; while(q.size()){ auto t=q.front(); q.pop(); int x=t.px,y=t.py,step=t.pstep; if(x==1&&y==1) return step; for(int i=0;i<12;i++){ int xx=x+dx[i],yy=y+dy[i]; if(xx>=1&&yy>=1){ if(!flag[xx][yy]){ flag[xx][yy]=true; q.push(point{xx,yy,step+1}); } } } } } int main(){ cin>>x1>>y1>>x2>>y2; cout< P1443 马的遍历
方法类似,只需要额外开一个二维答案数组存储步数即可
bfs(AC):
#include#include #include using namespace std; const int N=405; int n,m,x,y; int dx[8]={-2,-1,-2,1,-1,2,1,2}; int dy[8]={-1,-2,1,-2,2,-1,2,1}; int ans[N][N];//答案矩阵 bool flag[N][N]; struct point{ int px,py;//坐标 }; void bfs(int a,int b){ ans[a][b]=0; flag[a][b]=true; queue q; q.push(point{a,b}); while(q.size()){ auto t=q.front(); q.pop(); int x=t.px,y=t.py; for(int i=0;i<8;i++){ int xx=x+dx[i],yy=y+dy[i]; if(xx>=1&&xx<=n&&yy>=1&&yy<=m){ if(!flag[xx][yy]){ flag[xx][yy]=true; q.push(point{xx,yy}); ans[xx][yy]=ans[x][y]+1; } } } } return ; } int main(){ cin>>n>>m>>x>>y; memset(ans,-1,sizeof ans); memset(flag,0,sizeof flag); bfs(x,y); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cout< P1746 离开中山路
bfs(AC):
#include#include #include using namespace std; const int N=1005; int n,x1,x2,y1,y2; char a[N][N]; int dx[4]={-1,0,1,0}; int dy[4]={0,1,0,-1}; bool flag[N][N]; struct point{ int px,py,ps; }; int bfs(int bx,int by,int ex,int ey){ queue q; q.push(point{bx,by,0}); flag[bx][by]=true; while(q.size()){ auto t=q.front(); int x=t.px,y=t.py,step=t.ps; q.pop(); if(x==ex&&y==ey) return step; for(int i=0;i<4;i++){ int xx=x+dx[i],yy=y+dy[i]; if(xx>=1&&xx<=n&&yy>=1&&yy<=n){ if(a[xx][yy]=='0'){ if(!flag[xx][yy]){ flag[xx][yy]=true; q.push(point{xx,yy,step+1}); } } } } } return -1; } int main(){ cin>>n; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>a[i][j]; cin>>x1>>y1>>x2>>y2; cout< P3395 路障
这题整体思路不变,唯一要加的是唯一需要注意的是路障的处理,可开一个block数组存放在(x,y)位置路障将在什么时候放下,对于没有路障的点,直接走过,对于有路障的点,判断时间即可
bfs(AC):
#include#include #include using namespace std; const int N=1005; int T,n,a[N][N],block[N][N]; int dx[4]={-1,0,1,0}; int dy[4]={0,1,0,-1}; bool flag[N][N]; struct point{ int px,py,pt;//坐标,时间 }; bool bfs(){ queue q; q.push(point{1,1,0}); flag[1][1]=true; while(!q.empty()){ auto t=q.front(); int x=t.px,y=t.py,timee=t.pt; q.pop(); if(x==n&&y==n) return true; timee++; for(int i=0;i<4;i++){ int xx=x+dx[i],yy=y+dy[i]; if(xx>=1&&xx<=n&&yy>=1&&yy<=n){ if(a[xx][yy]==0&&!flag[xx][yy]){ flag[xx][yy]=true; if(!block[xx][yy]||timee<=block[xx][yy]){ q.push(point{xx,yy,timee}); } } } } } return false; } int main(){ cin>>T; while(T--){ memset(flag,0,sizeof flag); memset(block,0,sizeof block); memset(a,0,sizeof a); cin>>n; for(int i=1;i<=2*n-2;i++){ int a1,b1; cin>>a1>>b1; block[a1][b1]=i; } if(bfs()) cout<<"Yes"< P8673 [蓝桥杯 2018 国 C] 迷宫与陷阱
和 P2802 回家 类似,
三维状态数组+多种情况判断 bfs(AC):
#include#include #include using namespace std; const int N=1005; int n,k; char a[N][N]; int dx[4]={-1,0,1,0}; int dy[4]={0,1,0,-1}; //flag[x][y][z]:在剩余z个无敌时间时,有没有经过(x,y) bool flag[N][N][11]; struct point{ int px,py,ps,pk;//坐标,当前走的步数,剩余无敌的步数 }; int bfs(){ queue q; q.push({1,1,0,0}); flag[1][1][0]=true; while(!q.empty()){ auto t=q.front(); q.pop(); int x=t.px,y=t.py,step=t.ps,temp=t.pk; if(x==n&&y==n) return step;//到达终点,直接结束 for(int i=0;i<4;i++){ int xx=x+dx[i],yy=y+dy[i]; if(xx>=1&&xx<=n&&yy>=1&&yy<=n){//保证边界 if(a[xx][yy]!='#'){//墙是永远不可能经过的 //当前是未使用过的道具 if(a[xx][yy]=='%'&&!flag[xx][yy][k]){ flag[xx][yy][k]=true; q.push({xx,yy,step+1,k}); } //当前状态是无敌,道路和陷阱都可以走 if(temp>0&&!flag[xx][yy][temp-1]){ flag[xx][yy][temp-1]=true; q.push({xx,yy,step+1,temp-1}); } //当前状态不是无敌,道路可以走 if(temp==0&&a[xx][yy]=='.'&&!flag[xx][yy][0]){ flag[xx][yy][0]=true; q.push({xx,yy,step+1,0}); } } } } } return -1; } int main(){ cin>>n>>k; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>a[i][j]; cout< P1141 01迷宫
暴力bfs (超时未AC 70points):
#include#include #include using namespace std; const int N=1e3+5; char a[N][N]; bool flag[N][N]; int n,m,ans; int dx[4]={-1,0,1,0}; int dy[4]={0,-1,0,1}; int bfs(int tx,int ty){ ans=1; flag[tx][ty]=1; queue >q; q.push({tx,ty}); while(!q.empty()){ auto t=q.front(); int x=t.first,y=t.second; q.pop(); for(int i=0;i<4;i++){ int xx=x+dx[i],yy=y+dy[i]; if(xx>=1&&xx<=n&&yy>=1&&yy<=n){ if(!flag[xx][yy]&&a[xx][yy]!=a[x][y]){ flag[xx][yy]=1; ans++; q.push({xx,yy}); } } } } return ans; } int main(){ cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>a[i][j]; while(m--){ int ax,bx; cin>>ax>>bx; memset(flag,0,sizeof flag); cout< 优化思路:
联通块上每一个点的答案等于该联通块的数目
注意这里的联通块指的是四个方向上0,1相间隔的联通块,如下图,在搜 a[1][2] 时会经过6个点,而这6个点的答案是一样的,都是6,因此通过搜完联通块直接给联通块上的所有点存答案即可
因此可以考虑 dfs+记忆化搜索(AC):
#includeusing namespace std; const int N=1e3+5; char a[N][N];//地图 int ans[N][N];//答案数组为0表示没有搜过,大于0表示答案 int temp[N*N][2];//记忆化,存储坐标数组 int n,m,res; int dx[4]={-1,0,1,0}; int dy[4]={0,-1,0,1}; void dfs(int x,int y){ res++;//搜到,答案自增 //记忆化:存储坐标 temp[res][0]=x; temp[res][1]=y; //ans数组当前只起标记作用 ans[x][y]=1; for(int i=0;i<4;i++){ int xx=x+dx[i],yy=y+dy[i]; if(xx>=1&&xx<=n&&yy>=1&&yy<=n){ if(a[x][y]!=a[xx][yy]){ if(!ans[xx][yy]){ dfs(xx,yy); } } } } return ; } int main(){ cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>a[i][j]; while(m--){ int ax,bx; cin>>ax>>bx; res=0; //已经存储过答案直接输出 if(ans[ax][bx]) cout<
还没有评论,来说两句吧...