首页 > 试题广场 >

水雷阵列

[编程题]水雷阵列
  • 热度指数:92 时间限制:C/C++ 3秒,其他语言6秒 空间限制:C/C++ 128M,其他语言256M
  • 算法知识视频讲解
本题分两问,第一问占20分,第二问占10分。

一条小河(在笛卡尔坐标系中,用 y=0 和 y=100.0 表示其两岸),其中被敌人布了若干(N 个)电磁水雷,其感应半径为 R 。我方一只小船(不计体积和大小,看做一个点)只要和水雷的距离 L <= R 就会触发爆炸,那么(第1问)小船是否可以不被发现地闯过水雷阵?如果小船可以闯过水雷阵,那么(第二问)敌方最少还需要增补多少个(数量记为 M引爆半径为 D 的新型水雷才能对我方小船实时封锁?

如上图,假如只有1、2、3三个水雷(圆圈代表其触发范围),那结论为“否”(无法闯过);假如只有4、5、6、7四个水雷,那结论为“是”(可以闯过),如果新型水雷如上图和6号水雷相连的小圆,那么再需要1个就能封锁住小船了(M等于1)。


输入描述:
每个测试数据集包含 2~20 种情况,每种情况一行,每行包含 3+N*2 个数字(空格分隔),第1个数字为N表示有N个水雷(1<=N<=100),第2个数字为该组水雷的感应半径 R,第3个数字为新增水雷的感应半径 D,后面依次是N个水雷的中心坐标(xi, yi),其中0<yi<100.0(水雷当然都布在水里),0<xi<500.0。

共15个测试文件,其中前3个测试文件仅包含不超过2个水雷,前10个测试文件D等于0。


输出描述:
对应输入文件的情况,每种情况一行输出。
若D等于0,则输出 N(不能闯过)或 Y(可以闯过)
若D不等于0,则输出 N(不能闯过)或者数字 M(最小需要增补的水雷数)。
示例1

输入

3 10.0 0.0 13.3 14.2 15.7 25.0 33.3 59.8
1 70.0 0.0 40.5 60.5
2 30.0 30 40.5 35.5 65.6 64.5

输出

Y
N
2

说明

输入文件不明确给出行数,以文件结束为结束。
本题无需过分考虑浮点误差的影响。

备注:
注意:若D等于0,则无需计算第二问。
import sys
from queue import PriorityQueue
import math

def is_pass(R, sorted_locs):
    q = []
    temp_y = 0
    book=[0]*len(sorted_locs)
    for i in range(len(sorted_locs)):
        if sorted_locs[i][1] <= R:
            book[i]=1
            q.append(i)
        else:
            break

    while len(q)>0:
        loc = q.pop(0)
        temp_y = max(temp_y,sorted_locs[loc][1] + R)
        if temp_y>=100.0:
            break
        for i in range(0,len(sorted_locs)):
            if book[i]==1:
                continue
            if ((sorted_locs[loc][0]-sorted_locs[i][0])**2+(sorted_locs[loc][1]-sorted_locs[i][1])**2)**0.5<=2*R:
                q.append(i)
                book[i]=1
            if sorted_locs[i][1]>sorted_locs[loc][1]+2*R:
                break
    if temp_y>=100:
        return 'N'
    return 'Y'

def counter1(R, sorted_locs,D):
    book = [0] * len(sorted_locs)
    q = PriorityQueue()
    temp_y = 0
    res_count= math.ceil(100/(2*D))
    for i in range(len(sorted_locs)):
        if sorted_locs[i][1] < R:
            q.put((0, i))
        else:
            diff= math.ceil((sorted_locs[i][1]-R)/(2*D))
            q.put((diff, i))
    while not q.empty():
        diff, loc = q.get()
        x,y= sorted_locs[loc]
        if y+R>temp_y:
            temp_y = max(temp_y,y + R)
            if temp_y<=100.0:
                res_count = min(res_count, diff + math.ceil((100 - y - R) / (2 * D)))
            else:
                res_count = min(res_count, diff)
            if temp_y >= 100.0:
                break

        if temp_y>=100.0:
            break
        book[loc]=1
        for i in range(len(sorted_locs)):
            if book[i]==1:
                continue
            if ((x - sorted_locs[i][0]) ** 2 + (
                    y - sorted_locs[i][1]) ** 2) ** 0.5 < 2 * R:
                q.put((diff,i))

            else:
                diff_new = math.ceil((((x - sorted_locs[i][0]) ** 2 + (
                            y - sorted_locs[i][1]) ** 2) ** 0.5 - 2 * R) / (2 * D))
                q.put((diff+diff_new,i))
    return res_count

for line in sys.stdin:
    inputs = line.split()
    N, R, D = int(inputs[0]), float(inputs[1]), float(inputs[2])
    locs = [(float(inputs[i]), float(inputs[i + 1])) for i in range(3, len(inputs), 2)]
    sorted_locs = sorted(locs, key=lambda x: x[1])
    res= is_pass(R,sorted_locs)
    if D==0 :
        print(res)
    else:
        if  res=='N':
            print(res)
        else:
            res= counter1(R,sorted_locs,D)
            print(res)


其实这题就是宽度优先搜索,第一问是看能否搜到对边,第二问是看搜到对面中间需要加多少,注意需要记录以前访问过的,不然会一直循环导致超时,在代码中使用book标记访问过的点。Now,show my code!

发表于 2020-09-27 08:38:52 回复(0)
发表于 2020-08-30 09:38:53 回复(0)