Acwing 蓝桥杯备战(第四天 BFS和并查集专题)

注意:只有每个边的权值都相同时,才能用bfs求最短路。

走迷宫

请添加图片描述思路分析
求最短路,采用bfs算法

#include<bits/stdc++.h>
using namespace std;
const int N=110;
int g[N][N];
int dis[N][N];
typedef pair<int,int> PII;
#define x first
#define y second
int n,m;
int bfs(PII start,PII end){
    memset(dis,-1,sizeof dis);
    queue<PII> q;
    dis[start.x][start.y]=0;
    q.push(start);
    while(q.size()){
        auto a=q.front();
        q.pop();
        int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
        for(int i=0;i<4;i++){
            int x=a.x+dx[i];
            int y=a.y+dy[i];
            if(x<1||x>n||y<1||y>m) continue; //越界
            if(g[x][y]==1) continue; //障碍
            if(dis[x][y]!=-1) continue; //已经被扩展
            dis[x][y]=dis[a.x][a.y]+1;
            if(x==n&&y==m) return dis[x][y];
            q.push({x,y});
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    
    PII start,end;
    
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&g[i][j]);
        }
    }
    
    start={1,1},end={n,m};
    
    int distance=bfs(start,end);
    
    printf("%d",distance);
    return 0;
}

献给阿尔吉侬的花束

请添加图片描述

#include<bits/stdc++.h>
using namespace std;
const int N=210;
char g[N][N];
int dis[N][N];
typedef pair<int,int> PII;
#define x first
#define y second
int t,n,m;
int bfs(PII start,PII end){
    memset(dis,-1,sizeof dis);
    queue<PII> q;
    dis[start.x][start.y]=0;
    q.push(start);
    while(q.size()){
        auto a=q.front();
        q.pop();
        int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
        for(int i=0;i<4;i++){
            int x=a.x+dx[i];
            int y=a.y+dy[i];
            if(x<0||x>=n||y<0||y>=m) continue; //越界
            if(g[x][y]=='#') continue; //障碍
            if(dis[x][y]!=-1) continue; //被扩展过
            dis[x][y]=dis[a.x][a.y]+1;
            if(end==make_pair(x,y)) return dis[x][y];
            q.push({x,y});
        }
    }
    return -1;
}
int main(){
    PII start,end;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++){
            scanf("%s",g[i]);
        }
        
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(g[i][j]=='S') start={i,j};
                if(g[i][j]=='E') end={i,j};
            }
        }
        
        int distance=bfs(start,end);
        if(distance==-1) printf("oop!\n");
        else printf("%d\n",distance);
    }
    return 0;
}

水桶传递队列

请添加图片描述

#include<bits/stdc++.h>
using namespace std;
const int N=15;
char g[N][N];
int dis[N][N];
typedef pair<int,int> PII;
#define x first
#define y second
int bfs(PII start,PII end){
    memset(dis,-1,sizeof dis);
    queue<PII> q;
    q.push(start);
    dis[start.x][start.y]=0;
    while(q.size()){
        auto a=q.front();
        q.pop();
        int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
        for(int i=0;i<4;i++){
            int x=a.x+dx[i];
            int y=a.y+dy[i];
            if(g[x][y]=='R') continue;
            if(x<0||x>=10||y<0||y>=10) continue;
            if(dis[x][y]!=-1) continue;
            dis[x][y]=dis[a.x][a.y]+1;
            if(end==make_pair(x,y)) return dis[x][y];
            q.push({x,y});
        }
    }
}
int main(){
    PII start,end;
    
    for(int i=0;i<10;i++) scanf("%s",g[i]);
    
    for(int i=0;i<10;i++){
        for(int j=0;j<10;j++){
            if(g[i][j]=='L') start={i,j};
            if(g[i][j]=='B') end={i,j};
        }
    }
    
    int distance=bfs(start,end);
    
    printf("%d",distance-1);
    return 0;
}

红与黑

请添加图片描述思路分析
bfs实现FloodFill算法。

#include<bits/stdc++.h>
using namespace std;
const int N=25;
char g[N][N];
int n,m;
typedef pair<int,int> PII;
#define x first
#define y second
int bfs(int sx,int sy){
    queue<PII> q;
    q.push({sx,sy});
    int cnt=0;
    g[sx][sy]='#';
    int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
    while(q.size()){
        auto a=q.front();
        q.pop();
        cnt++;
        for(int i=0;i<4;i++){
            int x=a.x+dx[i];
            int y=a.y+dy[i];
            if(x<0||x>=n||y<0||y>=m) continue;
            if(g[x][y]!='.') continue;
            g[x][y]='#';
            q.push({x,y});
        }
    }
    return cnt;
}
int main(){
    int x,y;
    while(cin>>m>>n,n||m){
        for(int i=0;i<n;i++) cin>>g[i];
        
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(g[i][j]=='@'){
                    x=i;
                    y=j;
                }
            }
        }
        
        cout<<bfs(x,y)<<endl;
    }
    
    return 0;
}

思路分析
dfs实现FloodFill算法

#include<bits/stdc++.h>
using namespace std;
const int N=25;
char g[N][N];
int n,m;
int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
int dfs(int sx,int sy){
    int cnt=1;
    g[sx][sy]='#';
    for(int i=0;i<4;i++){
        int x=sx+dx[i];
        int y=sy+dy[i];
        if(x<0||x>=n||y<0||y>=m) continue;
        if(g[x][y]!='.') continue;
        g[x][y]='#';
        cnt+=dfs(x,y);
    }
    return cnt;
}
int main(){
    int x,y;
    while(cin>>m>>n,n||m){
        for(int i=0;i<n;i++) cin>>g[i];
        
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(g[i][j]=='@'){
                    x=i;
                    y=j;
                }
            }
        }
        
        cout<<dfs(x,y)<<endl;
    }
    
    return 0;
}

全球变暖

请添加图片描述

思路分析
先用bfs求出图中的所有连通块的数量,在bfs的同时,用bound变量记录会被淹没的大陆数量,total记录所有的大陆数量,如果两者相等则该岛屿会被完全淹没。

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
char g[N][N];
bool vis[N][N];
int n,cnt;
int total,bound;
typedef pair<int,int> PII;
#define x first
#define y second
void bfs(int sx,int sy){
    int dx[4]={0,-1,0,1},dy[4]={1,0,-1,0};
    queue<PII> q;
    q.push({sx,sy});
    vis[sx][sy]=1;
    while(q.size()){
        auto a=q.front();
        q.pop();
        total++;
        bool is_bound=false;
        for(int i=0;i<4;i++){
            int x=a.x+dx[i];
            int y=a.y+dy[i];
            if(x<0||x>=n||y<0||y>=n) continue;
            if(vis[x][y]) continue;
            if(g[x][y]=='.'){
                is_bound=1;
                continue;
            }
            vis[x][y]=1;
            q.push({x,y});
        }
        if(is_bound) bound++;
    }
}
int main(){
    scanf("%d",&n);
    
    for(int i=0;i<n;i++) scanf("%s",g[i]);
    
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            if(vis[i][j]==0&&g[i][j]=='#'){
                total=0,bound=0;
                bfs(i,j);
                if(total==bound)
                    cnt++;
            }
        }
    }
    
    printf("%d",cnt);
    return 0;
}

合并集合

请添加图片描述思路分析
并查集的板子,p[x]中存储的该结点的父节点,注意理解find函数中的路径压缩。

#include<iostream>
using namespace std;
const int N=1e5+10;
int p[N];
int n,m;
int find(int x){
    if(p[x]!=x) p[x]=find(p[x]);
    return p[x];
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) p[i]=i; //初始化,开始之前每个结点指向自己
    
    while(m--){
        string s;
        int a,b;
        cin>>s;
        if(s=="M"){
            cin>>a>>b;
            a=find(a),b=find(b);
            if(a!=b){
                p[a]=b;
                //a的祖先结点不等于b的祖先结点,两者不在一个集合,让a的祖先结点的父节点指向b的祖先
            }
        }else{
            cin>>a>>b;
            a=find(a),b=find(b);
            if(a==b){
                printf("Yes\n");
            }else{
                printf("No\n");
            }
        }
    }
    return 0;
}

连通块中点的数量

请添加图片描述思路分析
借助并查集维护一个连通块,如果a,b的祖先结点不相等说明不在同一个连通块内,
用cnt数组存储联通块中点的数量,如果将a和b之间连接一条边相当于合并两个集合,此时b连通块点的数量应该加上a中点的数量。

#include<iostream>
using namespace std;
const int N=1e5+10;
int p[N],cnt[N];
int n,m;
int find(int x){
    if(p[x]!=x) p[x]=find(p[x]);
    return p[x];
}
int main(){
    scanf("%d%d",&n,&m);
    
    for(int i=1;i<=n;i++){
        p[i]=i;
        cnt[i]=1;
    }
    
    while(m--){
        string s;
        cin>>s;
        int a,b;
        if(s=="C"){
            cin>>a>>b;
            a=find(a),b=find(b);
            if(a!=b){
                p[a]=b;
                cnt[b]+=cnt[a];
            }
        }else if(s=="Q1"){
            cin>>a>>b;
            a=find(a),b=find(b);
            if(a==b) printf("Yes\n");
            else printf("No\n");
        }else{
            scanf("%d",&a);
            a=find(a);
            printf("%d\n",cnt[a]);
        }
    }
    return 0;
}

合根植物

请添加图片描述请添加图片描述思路分析
借助并查集维护植物集合,如果两个植物不在同一个集合(就是不合根)则按要求合并两个集合,处理完毕后记录根节点的数目。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int p[N];
int n,k,m;
int find(int x){
	if(p[x]!=x) p[x]=find(p[x]);
	return p[x];
}
int ans;
int main(){
	scanf("%d%d",&n,&m);
	n*=m;
	scanf("%d",&k);
	for(int i=1;i<=n;i++) p[i]=i;
	while(k--){
		int a,b;
		scanf("%d%d",&a,&b);
		int pa=find(a),pb=find(b);
		if(pa!=pb){
			p[pa]=pb;
		}
	}
	for(int i=1;i<=n;i++){
		if(p[i]==i) ans++;
	} 
	printf("%d",ans);
	return 0;
}

修改数组

请添加图片描述

#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
int p[N];
int find(int x){
    if(p[x]!=x) p[x]=find(p[x]);
    return p[x];
}
int n;
int main(){
    scanf("%d",&n);
    for(int i=1;i<N;i++) p[i]=i;
    
    while(n--){
        int x;
        scanf("%d",&x);
        x=find(x);
        printf("%d ",x);
        p[x]=x+1;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值