·第一题:
呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第 i 层楼(1≤i≤N)上有一个数字 Ki(0≤Ki≤N)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如: 3, 3, 1, 2, 5 代表了 Ki(K1=3,K2=3,……),从 1 楼开始。在 1 楼,按“上”可以到 4 楼,按“下”是不起作用的,因为没有 −2 楼。那么,从 A 楼到 B 楼至少要按几次按钮呢?
输入格式
共二行。
第一行为三个用空格隔开的正整数,表示 N,A,B(1≤N≤200,1≤A,B≤N)。
第二行为 N 个用空格隔开的非负整数,表示Ki。
输出格式
一行,即最少按键次数,若无法到达,则输出 -1
。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,a,b;
int l[1000];
int vis[1000];
struct pos{
int level;
int steps;
};
void bfs(){
pos cur,nex;
cur.level=a;
cur.steps=0;
queue<pos>qu;
qu.push(cur);
vis[a]=1;
while(!qu.empty()){
cur=qu.front();
qu.pop();
if(cur.level==b){
printf("%d\n",cur.steps);
return;
}
nex.level=cur.level+l[cur.level];
nex.steps=cur.steps+1;
if(nex.level<=n){
if(vis[nex.level]==0){
vis[nex.level]=1;
qu.push(nex);
}
}
nex.level=cur.level-l[cur.level];
nex.steps=cur.steps+1;
if(nex.level>=1){
if(vis[nex.level]==0){
vis[nex.level]=1;
qu.push(nex);
}
}
}
printf("-1\n");
return;
}
int main(){
while(scanf("%d",&n)==1){
if(n==0) break;
scanf("%d%d",&a,&b);
for(int i=1;i<=n;i++){
scanf("%d",&l[i]);
vis[i]=0;
}
bfs();
}
return 0;
}
解题思路:
·本题为bfs的模板题,先定义一个结构体,它用来存储楼层和这是移动的第几次,从a层楼开始,先标记a已走过,即之后的过程中不能再到达a层。然后将a的层数赋值给cur.level,cur.step等于0;将cur放入定义的队列,并标记a已走过。当队列不为空时,取队首为cur,并把队首元素踢出队列,判断cur.level是否等于b,如果等于则输出cur.step并返回;如果不等,那么分两种情况:上升或下降,判断条件为不超过最高层或者最底层且到达的这一层之前未到达过。然后把符合条件的值作为nex放入队列,然后重复上述过程。如果队列清空仍无法到达,则输出-1.
第二题:
有一个n×m 的棋盘,在某个点(x,y) 上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步。
输出格式
一个 n×m 的矩阵,代表马到达某个点最少要走几步(不能到达则输出−1)。
输入格式
输入只有一行四个整数,分别为 n,m,x,y。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,x,y;
const int dx[8]={1,2,2,1,-1,-2,-2,-1};
const int dy[8]={2,1,-1,-2,-2,-1,1,2};
struct pos{
int u;
int v;
}cur;
int f[500][500];
bool vis[500][500];
int main(){
queue<pos>q;
cin>>n>>m>>x>>y;
memset(f,-1,sizeof(f));
memset(vis,false,sizeof(vis));
cur.u=x;
cur.v=y;
q.push(cur);
vis[x][y]=true;
f[x][y]=0;
while(!q.empty()){
cur=q.front();
int xx=cur.u;
int yy=cur.v;
q.pop();
for(int i=0;i<8;i++){
int u=xx+dx[i];
int v=yy+dy[i];
if(u<1||u>n||v<1||v>m||vis[u][v]==true) continue;
vis[u][v]=true;
cur.u=u;
cur.v=v;
q.push(cur);
f[u][v]=f[xx][yy]+1;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cout<<f[i][j]<<' ';
}
cout<<endl;
}
return 0;
}
解题思路:
本题思路与第一题类似,定义一个结构体来存储x,y坐标。因为马走日字格,所以定义dx,dy来表示马可能的走法。开始时将每一个位置都定义为为走过,并且都为-1(即无法到达)。之后将马的初始位置赋值给cur,然后把cur放入队列,并标记这个位置(防止重复走过),此位置的步数为0。之后当队列不为空时,队首赋值为cur,把cur的横纵坐标赋值给xx,yy;遍历dx,dy,判断各种走法是否可行(判断条件为是否越界和到达的点是否走过),如果符合则加入队列,并且(u,v)的步数为(xx,yy)的步数加一。然后输出整个矩阵即可。
第三题·:
现有一块大奶酪,它的高度为 hh,它的长度和宽度我们可以认为是无限大的,奶酪中间有许多半径相同的球形空洞。我们可以在这块奶酪中建立空间坐标系,在坐标系中,奶酪的下表面为 z = 0,奶酪的上表面为z=h。
现在,奶酪的下表面有一只小老鼠 Jerry,它知道奶酪中所有空洞的球心所在的坐标。如果两个空洞相切或是相交,则 Jerry 可以从其中一个空洞跑到另一个空洞,特别地,如果一个空洞与下表面相切或是相交,Jerry 则可以从奶酪下表面跑进空洞;如果一个空洞与上表面相切或是相交,Jerry 则可以从空洞跑到奶酪上表面。
位于奶酪下表面的 Jerry 想知道,在不破坏奶酪的情况下,能否利用已有的空洞跑 到奶酪的上表面去?
空间内两点P1(x1,y1,z1)、P2(x2,y2,z2) 的距离公式如下:
\mathrm{dist}(P_1,P_2)=\sqrt{(x_1-x_2)^2+(y_1-y_2)^2+(z_1-z_2)^2}
输入格式
每个输入文件包含多组数据。
第一行,包含一个正整数 TT,代表该输入文件中所含的数据组数。
接下来是 TT 组数据,每组数据的格式如下: 第一行包含三个正整数n,h,r,两个数之间以一个空格分开,分别代表奶酪中空洞的数量,奶酪的高度和空洞的半径。
接下来的 n 行,每行包含三个整数 x,y,z,两个数之间以一个空格分开,表示空洞球心坐标为(x,y,z)。
输出格式
T 行,分别对应 T 组数据的答案,如果在第 i 组数据中,Jerry 能从下表面跑到上表面,则输出 Yes
,如果不能,则输出 No
。
代码:
#include<bits/stdc++.h>
using namespace std;
int t;
bool m;
double dis(int x1,int x2,int y1,int y2,int z1,int z2){
double d;
d=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2));
return d;
}
struct s{
long long a,b,c;
}con[50],pos,p[1005];
map<s,bool> vis;
bool operator<(const s& lhs,const s& rhs){
return lhs.c<rhs.c;
}
bool det(int n,int h,int r){
queue<s>q;
for(int i=1;i<=n;i++){
cin>>p[i].a>>p[i].b>>p[i].c;
vis[p[i]]=false;
}
sort(p+1,p+1+n);
for(int i=1;i<=n;i++){
if(p[i].c-r<=0) q.push(p[i]);
}
while(!q.empty()){
pos=q.front();
int x=pos.a,y=pos.b,z=pos.c;
q.pop();
vis[pos]=true;
if(z+r>=h) return true;
for(int i=1;i<=n;i++){
if(dis(x,p[i].a,y,p[i].b,z,p[i].c)<=2*r&&vis[p[i]]==false){
vis[p[i]]=true;
pos.a=p[i].a,pos.b=p[i].b,pos.c=p[i].c;
q.push(pos);
}
}
}
return false;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>t;
queue<s>q;
for(int i=1;i<=t;i++){
cin>>con[i].a>>con[i].b>>con[i].c;
m=det(con[i].a,con[i].b,con[i].c);
if(m==true) cout<<"Yes";
else cout<<"No";
cout<<endl;
}
return 0;
}
解题思路:
本题特殊之处为三维坐标系,且可能有多个可能的出发点。首先将所有与地面相切或相交的洞的圆心坐标放入队列,然后标记为已走过。取队首元素赋值给pos,然后把队首踢出,之后遍历所有洞,如果相切或相交且为走过,则替换坐标并加入队列。若途中z坐标+r大于h,则返回true;若队列元素清空前仍无法到达,则返回false。
第四题:
由数字 0 组成的方阵中,有一任意形状闭合圈,闭合圈由数字 1 构成,围圈时只走上下左右 4 个方向。现要求把闭合圈内的所有空间都填写成 2。例如:6×6 的方阵(n=6n=6),涂色前和涂色后的方阵如下:
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 0 0 1
1 1 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1
0 0 0 0 0 0
0 0 1 1 1 1
0 1 1 2 2 1
1 1 2 2 2 1
1 2 2 2 2 1
1 1 1 1 1 1
输入格式
每组测试数据第一行一个整数n(1≤n≤30)。
接下来 n 行,由 0 和 1 组成的n×n 的方阵。
方阵内只有一个闭合圈,圈内至少有一个 0。
输出格式
已经填好数字 2 的完整方阵。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,a[40][40];
int dx[5]={1,0,0,-1,0};
int dy[5]={0,1,-1,0,0};
queue<pair<int ,int > > q;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>a[i][j];
}
}
for(int i=0;i<=n+1;i++){
for(int j=0;j<=n+1;j++){
if(a[i][j]==0) a[i][j]=2;
}
}
q.push(make_pair(0,0));
while(!q.empty()){
int x=q.front().first;
int y=q.front().second;
q.pop();
a[x][y]=0;
for(int i=0;i<4;i++){
if(a[x+dx[i]][y+dy[i]]==2) q.push(make_pair(x+dx[i],y+dy[i]));
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<a[i][j]<<' ';
}
cout<<endl;
}
return 0;
}
解题思路:
本题要把1形成的空间内的赋值为2,可以先把所有的0都赋值为2,然后把封闭空间外的再赋值为0。这道题中要注意如果直接按题目的方阵搜索,会有遗漏,可以扩大一圈在搜索,即从(0,0)到(6,6)。把所有值不为1的点赋值为2.然后定义dx,dy来表示移动方式。把(0,0)放入队首,然后赋值给x,y,之后踢出。把(x,y)赋值为0,之后遍历dx,dy。如果移动后的坐标仍为2,则把这个移动后的坐标传入队列。最后输出1~6的矩阵即可。