蓝桥杯基础技能树题解一(1~5)
1001-地、颜色、魔法
解:
在n*m的矩形中有两种点','和'#',分别表示没用标记和有标记。被标记点完全包围的点(即从该点走到边界一定要经过至少一个标记点)同样视为被标记点。统计所有“标记点”的数量。
很基础的图遍历,用深度优先或者广度优先去遍历整张图都可以。由题意可以知道,一个点如果在矩阵的边界,那么它一定不是标记点。所以枚举每一个矩阵的边界点,若边界点是'.',则以该点为起点遍历整张图并在原图中染色。我们将可以走到的点重新标记为‘@’,则剩下来的‘#’和'.'就都是标记点了。最后用循环遍历整张图完成操作。
对于存图,由于1<=n,m<=1e6,所以直接开二维数组mp[maxn][maxm]需要1e12的空间,此时已经超出了程序默认栈的大小,所以需要用一些巧妙的办法去存图。对于一维的字符串可以用string来存,二维字符串就可以用vector<string>这样套娃了。
代码:
#include<bits/stdc++.h> using namespace std; int n,m,ans=0,d[][2]={{0,1},{0,-1},{-1,0},{1,0}}; vector<string> mp; bool check(int x,int y) { return 0<=x&&x<n&&0<=y&&y<m&&mp[x][y]=='.'; } void dfs(int x,int y) { mp[x][y]='@'; for(int i=0;i<4;i++) { int tx=x+d[i][0]; int ty=y+d[i][1]; if(check(tx,ty)) dfs(tx,ty); } } int main() { cin>>n>>m; for(int i=0;i<n;i++) { string s; cin>>s; mp.push_back(s); } for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { if( (i==0||i==n-1) && (mp[i][j]=='.') ) dfs(i,j); if( (j==0||j==m-1) && (i!=0||i!=n-1) && (mp[i][j]=='.') ) dfs(i,j); } } for(int i=0;i<n;i++) for(int j=0;j<m;j++) ans+=(int)(mp[i][j]!='@'); cout<<ans<<endl; return 0; }
1002-扫雷
解:
扫雷都玩过吧。。。没有雷无限延伸,有数字则显示最边界的一个数字。即操作某一个格子以后,从该点开始遍历图,把所有的空格子都显示出来,并且在显示一个数字后返回。bfs和dfs都可以,记得标vis表示这个点是否走过了。
具体实现见代码,都是一样的东西就不细说了。注意若从下标0开始存图,存图坐标与输入坐标可能会差1,这是一个初学者常犯的错误。
代码:
#include<bits/stdc++.h> using namespace std; const int N=505; char s[N][N]; int n,m,k,x,y,f; void solve() { f=1; cin>>n>>m>>k; for(int i=0;i<n;i++) cin>>s[i]; for(int i=1;i<=k;i++) { cin>>x>>y; x--,y--; if(!f) continue; if(s[x][y]=='.') s[x][y]='0'; if(s[x][y]=='*') { cout<<"Game over in step "<<i<<endl; f=0; } } if(f) for(int i=0;i<n;i++) cout<<s[i]<<endl; return; } int main() { int __; cin>>__; while(__--) solve(); }
1003-wyh的迷宫
解:
典中典dfs/bfs,如果要求最短路径可以选择bfs来做。不然这种裸体两个算法效率差不多,取决于出题人怎么出这个图。板子题就直接看代码吧。。
代码:
#include<bits/stdc++.h> using namespace std; const int N=1005; int n,m,d[][2]={{0,1},{0,-1},{-1,0},{1,0}}; char mp[N][N]; bool check(int x,int y){return 0<=x&&x<n&&0<=y&&y<m&&mp[x][y]!='x';} bool dfs(int x,int y) { if(mp[x][y]=='t') return 1; for(int i=0;i<4;i++) { int tx=x+d[i][0]; int ty=y+d[i][1]; if(check(tx,ty)) { mp[x][y]='x'; if(dfs(tx,ty)) return 1; } } return 0; } void solve() { cin>>n>>m; for(int i=0;i<n;i++) cin>>mp[i]; for(int i=0;i<n;i++) for(int j=0;j<m;j++) if(mp[i][j]=='s') { if(dfs(i,j)) puts("YES"); else puts("NO"); return; } return; } int main() { int __; cin>>__; while(__--) solve(); return 0; }
1004-迷宫
解:
又是个走迷宫题,写得多了应该就可以发现,对于这种图遍历问题都有差不多固定的解决方案,大同小异。
对于本题来说,有两种方案。方案一,从起点开始直接冲终点,把钥匙和门视为不可以走的墙,得到ans1。方案二,从起点去找钥匙,拿了钥匙去开门,再从门去终点,一共要有3次起止,加起来得到ans2。我们去min(ans1,ans2)即可得到答案。具体如何实现bfs不过多赘述,可以去看相关视频或博客自学板子。
代码:
#include<bits/stdc++.h> using namespace std; const int N=505; int h,w,r0,r1,r2,r3; int sx,sy,ex,ey,kx,ky,dx,dy; char mp[N][N]; bool vis[N][N]; int d[][2]={{0,1},{0,-1},{-1,0},{1,0}}; struct node{int x,y,step;}; int bfs(int x1,int y1,int x2,int y2) { queue<node> q; memset(vis,0,sizeof(vis)); vis[x1][y1]=1; q.push({x1,y1,0}); while(!q.empty()) { node tmp=q.front(); q.pop(); if(tmp.x==x2&&tmp.y==y2) return tmp.step; for(int i=0;i<4;i++) { int tx=tmp.x+d[i][0]; int ty=tmp.y+d[i][1]; if( 0<=tx&&tx<h && 0<=ty&&ty<w && mp[tx][ty]=='.'&&!vis[tx][ty] ) { vis[tx][ty]=1; q.push({tx,ty,tmp.step+1}); } } } return -1; } int main() { cin>>h>>w; for(int i=0;i<h;i++) { cin>>mp[i]; for(int j=0;j<w;j++) { if(mp[i][j]=='S') sx=i,sy=j; if(mp[i][j]=='E') ex=i,ey=j; if(mp[i][j]=='K') kx=i,ky=j; if(mp[i][j]=='D') dx=i,dy=j; } } mp[sx][sy]=mp[ex][ey]=mp[kx][ky]='.'; r0=bfs(sx,sy,ex,ey);//直接找终点 r1=bfs(sx,sy,kx,ky);//起点找钥匙 mp[dx][dy]='.'; r2=bfs(kx,ky,dx,dy);//钥匙找锁门 r3=bfs(dx,dy,ex,ey);//锁门找终点 if(r0==-1) { if(r1==-1||r2==-1||r3==-1) puts("-1"); else cout<<r1+r2+r3<<endl; } else cout<<min(r0,r1+r2+r3)<<endl; return 0; }
1005-桃花
解:
本题给出了n个点和n-1条双向边,对于这样的一个图求最长的一条链的长度。因为这个图是一个树形图,所以求的东西有个名称叫树的直径。顾名思义,就是在树的两端各取一点,他们连起来最长就是树的直径。
不难观察到,假设树的其中一条直径由节点a,b作为端点构成,那么从图中的任意一个点k去找以k为起点的最长链,一定可以找点端点b(从b反向去推到k可以更好理解)。再从b去推最长链就可以找到节点a了。
对于实现这个推链的过程,在固定起点的时候,跑bfs或者dfs都可以。
综上,本题的解题思路变成了:从随机点(假设是点1)开始跑一遍搜索得到终点b,再从b开始跑一遍搜索得到终点a。搜索过程中维护max,最后输出即可。
另外本题有一个卡时间的地方,就是存图。如果使用vector<int> v[maxn]这样vector存邻接表的方法去存图,很可能因为stl的巨大常数导致超时。所以推荐使用链式前向星去存图。邻接表可以靠评测机抖动卡过去,链式前的效率更高,省了400ms,乱杀。另外看到有清华爷写了一个更妙的办法,一并贴在下方供学习。
ps:图的存储包括了二维邻接矩阵,邻接表,链式前向星等等。建议自学。
#include<bits/stdc++.h> const int N=1000009; using namespace std; typedef long long int ll; struct as{ int y,next; }a[2000009]; ll s[N],dis[N],ex=1,cnt=0,ans=-1; bool vis[N]; void add(int x,int y) { a[++cnt].y=y; a[cnt].next=s[x]; s[x]=cnt; } void bfs() { memset(vis,0,sizeof vis); queue<int> p; p.push(ex); dis[ex]=1; int x,y; while(!p.empty()) { x=p.front(); p.pop(); vis[x]=1; if(ans<dis[x]){ ans=dis[x]; ex=x; } for(int i=s[x];i!=0;i=a[i].next) { y=a[i].y; if(!vis[y]) { dis[y]=dis[x]+1; p.push(y); } } } } int main() { int n,x,y; scanf("%d",&n); for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } bfs(); bfs(); printf("%lld",ans); return 0; }
#include<bits/stdc++.h> using namespace std; int n,m,i,j,k,h[1000005],ne[2000005],p[2000005],f[1000005],d[1000005]; void dfs(int x) { for(int i=h[x];i;i=ne[i])if(p[i]!=f[x]) { f[p[i]]=x; d[p[i]]=d[x]+1; dfs(p[i]); } } int main() { scanf("%d",&n); for(i=1;i<n;i++) { scanf("%d%d",&j,&k); p[++m]=k; ne[m]=h[j]; h[j]=m; p[++m]=j; ne[m]=h[k]; h[k]=m; } dfs(1); for(i=j=1;i<=n;i++)if(d[i]>d[j])j=i; f[j]=d[j]=0; dfs(j); for(i=1,j=0;i<=n;i++)j=max(j,d[i]); cout<<j+1<<endl; return 0; }

浙公网安备 33010602011771号