ACM算法题第六周

·第一题:

呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第 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的矩阵即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值