本文章主要记录第六周做题的思路。如有错误,请大佬斧正。若大佬有更好的思路,也请指教。
本周涉及BFS广度优先搜索、队列。
一、P1135 奇怪的电梯
思路:
底层思路其实是一个二分树:
假设电梯的路径如上,因为要找最短,所以每次到过的楼层都要做标记,以前到过的楼层,以后就不需要去了,所以五楼被打岔了。每次可以去的楼层都被放入队列中,表示这是可以去的,以后就会以队列中的楼层为出发点,继续找下一个可以去的楼层,直到到达目标楼层。
代码:
#include<bits/stdc++.h>
using namespace std;
struct pos{
int level,steps;
};
int a[201],vis[202]={0};
int main()
{
int n,start,end;
pos cur,nex;
queue<pos> qu;
cin>>n>>start>>end;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
//存入起始楼层
cur.level=start;
cur.steps=0;
qu.push(cur);
while(!qu.empty())
{
//更新cur的楼层
cur.level=qu.front().level;
cur.steps=qu.front().steps;
//判断队首楼层是否为end
if(cur.level==end)
{
cout<<cur.steps<<endl;
return 0;
}
//删去队首楼层
qu.pop();
//添加cur向上的楼层
nex.level=cur.level+a[cur.level];
nex.steps=cur.steps+1;
if(nex.level<=n)
{
if(vis[nex.level]==0)
{
vis[nex.level]=1;
qu.push(nex);
}
}
//添加cur向下的楼层
nex.level=cur.level-a[cur.level];
nex.steps=cur.steps+1;
if(nex.level>=1)
{
if(vis[nex.level]==0)
{
vis[nex.level]=1;
qu.push(nex);
}
}
}
//如果没有到达目标楼层
cout<<"-1"<<endl;
}
二、P1443 马的遍历
思路:
与上一题差不多,只是在找下一个能去的时候,用a、b两个数组和for循环简化了。
代码:
#include<bits/stdc++.h>
using namespace std;
struct pos{
int x,y,steps;
};
int main()
{
int n,m,xsta,ysta;
cin>>n>>m>>xsta>>ysta;
int bu[n+1][m+1];
memset(bu, -1, sizeof(bu));
pos cur,nex;
queue<pos> qu;
//定义马能走的所有路径
int a[9]={0,1,1,-1,-1,2,2,-2,-2},
b[9]={0,2,-2,2,-2,1,-1,1,-1};
//输入起始坐标
nex.steps=0;
nex.x=xsta;
nex.y=ysta;
qu.push(nex);
bu[nex.x][nex.y]=0;
while(!qu.empty())
{
//重新定义cur当前状态
cur.steps=qu.front().steps;
cur.x=qu.front().x;
cur.y=qu.front().y;
//删除队首元素
qu.pop();
//添加cur能到达的所有没去过位置,并输出用的步数
for(int i=1;i<=8;i++)
{
nex.x=cur.x+a[i];
nex.y=cur.y+b[i];
if(bu[nex.x][nex.y]==-1)
if(nex.x>=1&&nex.x<=n&&nex.y>=1&&nex.y<=m)
{
nex.steps=cur.steps+1;
bu[nex.x][nex.y]=nex.steps;
qu.push(nex);
}
}
}
//输出步数的矩阵
for(int i=1;i<=n;i++)
{
for(int z=1;z<=m;z++)
{
cout<<bu[i][z]<<" ";
}
cout<<endl;
}
}
三、P3958 [NOIP2017 提高组] 奶酪
思路:
小心最底下的洞不与下表面相连的情况。
我的走法是比较莽的,只要下个洞能走且没走过,就继续向下走。
代码:
#include<bits/stdc++.h>
using namespace std;
struct pos{
long long x,y,z,vis;
};
pos f[1001];
long long t,n,h,r,no;
void run(int yy)
{
//判断是否爬到上面了
if(f[yy].z+r>=h)
{
no=yy;
return;
}
//找下一个连通的洞
for(int i=1;i<=n;i++)
{
//走过的洞不要
if(f[i].vis==1)
{
continue;
}
else if(4*r*r>=(f[yy].x-f[i].x)*(f[yy].x-f[i].x)
+(f[yy].y-f[i].y)*(f[yy].y-f[i].y)
+(f[yy].z-f[i].z)*(f[yy].z-f[i].z))
{
f[i].vis=1;
//从这个洞接着向上爬
run(i);
}
}
}
int main()
{
cin>>t;
for(int ii=0;ii<t;ii++)
{
no=0;
cin>>n>>h>>r;
for(int i=1;i<=n;i++)
{
cin>>f[i].x>>f[i].y>>f[i].z;
f[i].vis=0;
}
//找出连接下层的洞
for(int i=1;i<=n;i++)
{
if(f[i].z-r<=0)
{
f[i].vis=1;
//从此开始向上爬
run(i);
}
}
if(no==0)
{
cout<<"No"<<endl;
}
else cout<<"Yes"<<endl;
}
}
四、P1162 填涂颜色
思路:
找圈里面的0不好找,那为什么不反着来?找圈外的0.
其实,还可以更简单一些,就是只要是边缘的0及与其相连的0都是圈外的。
代码:
#include<bits/stdc++.h>
using namespace std;
struct pos{
int s,xia,z,you,zhi;
};
int main()
{
pos a[31][31];
int n;
cin>>n;
for(int x=1;x<=n;x++)
{
for(int y=1;y<=n;y++)
{
cin>>a[x][y].zhi;
a[x][y].s=a[x][y].xia=a[x][y].z=a[x][y].you=0;
}
}
for(int x=2;x<n;x++)
{
for(int y=2;y<n;y++)
{
if(a[x][y].zhi==0)
{
for(int ss=1;ss<y;ss++)
{
if(a[x][ss].zhi==1)
{
a[x][y].s++;
}
}
for(int xx=y+1;xx<n+1;xx++)
{
if(a[x][xx].zhi==1)
{
a[x][y].xia++;
}
}
for(int zz=1;zz<x;zz++)
{
if(a[zz][y].zhi==1)
{
a[x][y].z++;
}
}
for(int yy=x+1;yy<n+1;yy++)
{
if(a[yy][y].zhi==1)
{
a[x][y].you++;
}
}
}
}
}
for(int x=1;x<=n;x++)
{
for(int y=1;y<=n;y++)
{
if(a[x][y].zhi==0) a[x][y].zhi=2;
}
}
for(int x=1;x<=n;x++)
{
for(int y=1;y<=n;y++)
{
if(a[x][y].zhi==2)
if(a[x][y].s==0||a[x][y].xia==0||a[x][y].you==0||a[x][y].z==0)
{
a[x][y].zhi=0;
}
}
}
for(int x=1;x<=n;x++)
{
for(int y=1;y<=n;y++)
{
if(a[x][y].zhi==2)
if(a[x+1][y].zhi==0||a[x][y+1].zhi==0||a[x-1][y].zhi==0||a[x][y-1].zhi==0)
{
a[x][y].zhi=0;
}
}
}
for(int x=1;x<=n;x++)
{
for(int y=1;y<=n;y++)
{
cout<<a[x][y].zhi<<" ";
}
cout<<endl;
}
}