Уничтожь их лазерами

21

Вступление

Арена представляет собой равнину, усеянную небоскребами, которую враги используют для укрытия. Вы и ваши враги стреляете друг в друга лазерами. Все вы несете реактивные ранцы, позволяющие полет.

Какие враги вы можете ударить с помощью лазера, а какие скрываются?

проблема

Во-первых, размер арены задается целым числом nв одной строке. Следующие nстроки содержат nцелые числа в строке, разделенные пробелом. Каждое целое число представляет высоту здания в этом месте. Каждое здание представляет собой прямоугольное тело, 1 единица на 1 единицу по высоте.

Далее, ваше местоположение дается на одной линии , как три числа с плавающей точкой x, y, z.

Наконец, количество врагов задается целым числом mв одной строке. Следующие mстроки содержат три числа с плавающей точкой на строку, разделенные пробелом. Они представляют x, yи zкоординаты противника. Система координат определяется следующим образом:

  • x измеряется слева направо в вводе города
  • y измеряется сверху вниз
  • z измеряется с нуля

Для каждого врага, если от вас до этого противника можно провести беспрепятственную линию, выведите положительное целое число. В противном случае выведите отрицательное целое число. Отдельные выходы новой строкой.

Пример ввода

Комментарии, обозначенные знаком «#», помогут вам быстро увидеть, что делает каждая строка. Они не будут присутствовать в фактическом вводе.

5              # Size of the map
0 0 0 0 0      # Buildings
0 0 0 0 0      # Buildings
4 4 4 4 4      # Buildings
0 0 0 0 0      # Buildings
0 0 0 0 0      # Buildings
2.5 0.0 4.0    # Your location
3              # Number of enemies
2.5 5.0 0.1    # Enemy location
2.5 5.0 5.0    # Enemy location
0.0 2.7 4.5    # Enemy location

Образец вывода

Для приведенного выше примера ввода мы выводим следующее:

-1
1
1

Предположения

  • 0 n<<100
  • 0 m<<100
  • 0 <= x<=n
  • 0 <= y<=n
  • 0 <= z<n
  • Игроки не будут находиться внутри или внутри угла, края или стороны здания
  • Ваша линия обзора противника никогда не будет касаться угла, края или стороны здания
  • Игрок не является препятствием
Rainbolt
источник
Рад видеть это из песочницы :)
Timtech
7
Если я не могу уничтожить врага, могу ли я присоединиться к ним?
Джон Дворак
@ user80551 Извините, мне пришлось откатить ваши правки на заголовок, потому что неправильное написание было преднамеренным. Поищи в Гугле.
Rainbolt
@Rusher Ой, извини, IDK что
user80551
4
Связанный: youtube.com/watch?v=NKTpWi5itOM
qwr

Ответы:

5

Perl, 301 296 282

Редактировать 2: На самом деле, соревнование или нет, нет причин не играть в гольф немного дальше. Проверьте это онлайн .

Редактировать: пара скобок пропала, более простое регулярное выражение для проверки ненулевого целого числа.

С переводом строки и отступом для удобства чтения:

sub i{<>=~/\S+/g}
@b=map[i],@r=0..<>-1;
print.1<=>(map{
    @a[1,0,2,4,3]=@a;
    @b=map{$i=$_;[map$b[$_][$i],@r]}@r;
    grep$a[3]
        &&($k=(($x=$_)-$a[0])/$a[3])**2<=$k
        &&pop[sort map@{$b[$_]}[$x-!!$x,$x],
                   ($_=$a[1]+$k*$a[4]),$_-/^\d+$/]
           >=$a[2]+$k*$a[5]
    ,@R=@r
}@a=map$_-shift@v,i,@u=@v=@$_),$/for([i])x<>

Требуется 5.14из-за скалярного (ссылка на массив) аргумента для pop.

user2846289
источник
Можете ли вы объяснить свое решение немного? Я не проверял и не использовал Perl, поэтому некоторые комментарии были бы хорошими.
WorldSEnder
@WorldSEnder, схема алгоритма следующая. Прямая линия PEсоединяет две точки в трехмерном пространстве: «Игрок» (X1Y1Z1) и «Враг» (X2Y2Z2). Его проекция на (XY)плоскость пересекает некоторые линии сетки, т. Е. Целые числа x = constили y = constтакие как X1 < x < X2или Y1 < y < Y2(при условии, что, например X1 < X2, но это не важно). Координаты x yэтих пересечений можно легко найти, и, следовательно, zкоординаты точки на PEлинии тоже.
user2846289
(продолжение) С другой стороны, для любых x yкоординат мы знаем высоту hздания (точнее, максимальную высоту до 4 зданий, которые имеют общую x yточку). Враг может быть убит, если (и только если) h < zдля всех упомянутых выше «точек пересечения». Реализация - это некоторая базовая арифметика, а также несколько трюков с Perl для игры в гольф. Расшифровка деталей того, как я это сделал месяц назад, займет некоторое время :-).
user2846289
Argh, как я вижу, есть ошибка в последней (5-й) ревизии: индексы для элементов @aмассива в grepвыражении должны появляться в порядке 0,3,0,4,1,5,2вместо 3,0,3,1,4,2,5- извините.
user2846289
Хорошо, лучше поздно, чем никогда, и чтобы закончить с этим все, вот прокомментированная версия.
user2846289
3

Python 2,7 - 429 420 308 308 символов

Я думал об этой задаче скорее как о математической задаче, чем о коде, так что не будьте слишком резкими, если я пропустил некоторые очевидные оптимизации. В любом случае, вот код:

b=lambda:raw_input().split()
m=map
d=range(input())
h=[m(int,b())for _ in d]
x,y,z=m(float,b())
for e,f,g in[m(float,b())for _ in[1]*input()]:o=lambda x,y,u,v,i,j:i<=x+u/v*(j+1-y)<=i+1<[]>z+(g-z)/v*(j+1-y)<=max(h[i][j:j+2])if v else 0;print 1-2*any(o(x,y,e-x,f-y,j,i)+o(y,x,f-y,e-x,i,j)for j in d for i in d)

Это должно работать для краевых и угловых случаев (каламбур непреднамеренный) и довольно солидно. Выход за предоставленный пример:

-1
1
1

И вот «краткое» объяснение:

fast_read = lambda : raw_input().split() # define a helper
# m = map another helper
grid_range = range(input())
houses = [map(int, fast_read()) for _ in grid_range]
# 'map(int,...)' is a shorter version of '[int(a) for a in ...]'
pos_x,pos_y,pos_z = map(float, fast_read()) # read the player position
# the following loops through all enemy coordinates
for ene_x, ene_y, ene_z in [map(float,fast_read()) for _ in[1]*input()]:
    vec_z = ene_z - pos_z
    # is_hit macro uses vector math to detemine whether we hit a specific wall
    # wallhit -> 1
    # no wallhit -> 0
    is_hit = lambda pos_x, pos_y, vec_x, vec_y, co_x, co_y:\
        (co_x <= pos_x + vec_x/vec_y * (co_y + 1 - pos_y) <= co_x + 1 # check if hit_x is good
        < [] > # an effective and
        pos_z + (ene_z - pos_z)/vec_y * (co_y + 1 - pos_y) <= max(houses[co_x][co_y:co_y + 2]) # check if hit_z is good
        if vec_y else 0) # if vec_y is 0 we can't hit the wall parallel to y
    print (.5 - # can hit -> 0.5 - 0 = 0.5, hit -> 0.5 - 1 = -0.5
            any( # if we hit any wall
                # we swap x and y-coordinate because we read them "incorrect"
                is_hit(pos_x, pos_y, ene_x-pos_x, ene_y-pos_y, cur_y, cur_x) # check for hit in x-direction
                + # effective 'or'
                is_hit(pos_y, pos_x, ene_y-pos_y, ene_x-pos_x, cur_x, cur_y) # check for hit in y-direction
                    for cur_y in grid_range # loop y
                for cur_x in grid_range)) # loop x

Я предполагаю, что это полно недостатков. Кстати, я сохранил символы при вложении (первый уровень - это один пробел, второй - одна вкладка, затем одна вкладка и пробел ...). Я надеюсь, что после всего этого ответа можно указать способ сделать это.

WorldSEnder
источник
Я только что понял, что выборочный ввод был неверен, потому что один из врагов был расположен прямо на земле, что технически является вершиной здания нулевой высоты, что, как я обещал, не произойдет. Ваша заявка проходит исправленный тестовый пример, но не проходит - ideone.com/8qn3sv . Можете ли вы проверить мой контрольный пример? Я мог что-то упустить или, возможно, моя система координат неясна.
Рейнболт
Нет, просто вектор проходит через углы ... теперь я знаю, почему вы пообещали предположения 6 и 7 :)
WorldSEnder
Кстати, я вывожу отрицательное число с плавающей точкой, но это можно исправить с помощью 2 дополнительных символов ( print 1-2*...вместо print.5-...), так что это не такая большая разница, я думаю
WorldSEnder
Вы прошли несколько тестов, которые я придумал. Хорошая работа! Вы все еще должны пойти дальше и заставить его печатать целые числа, чтобы соответствовать спецификации.
Rainbolt
1
Принимать ваш ответ, пока кто-то не придумает лучшего решения. Я не думаю, что они будут. Очень редко кто-нибудь возвращается к старым решенным задачам. Вы должны играть в гольф больше! Похоже, вы знаете свои вещи. :)
Rainbolt
2

С - 2468

Совсем не игра в гольф, но, надеюсь, это отправная точка для более интересных реализаций. Реализация intersectвзломана сильно от Адриана Боинга . Его псевдокод был неполным, но его объяснение математики было неоценимо. Основная идея заключается в том, что вы берете линию от игрока к цели и обрезаете ее по всем стенам каждого здания, обновляя длину для каждой стены. Оставшаяся длина - это часть внутри здания, поэтому, если она равна нулю, пересечения не было.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct
{
    float x;
    float y;
    float z;
} vec3;

float
dot(vec3 a, vec3 b)
{
    return a.x * b.x + a.y * b.y + a.z * b.z;
}

vec3
scale(float s, vec3 a)
{
    vec3 r;
    r.x = s * a.x;
    r.y = s * a.y;
    r.z = s * a.z;
    return r;
}

vec3
add(vec3 a, vec3 b)
{
    vec3 r;
    r.x = a.x + b.x;
    r.y = a.y + b.y;
    r.z = a.z + b.z;
    return r;
}

int
intersect(vec3 a, vec3 b, vec3 *normals, vec3 *points, int nnormals)
{
    vec3 ab = add(b, scale(-1, a));
    float tfirst = 0;
    float tlast = 1;
    int i;
    for(i = 0; i < nnormals; i++)
    {
        float d = dot(normals[i], points[i]);
        float denom = dot(normals[i], ab);
        float dist = d - dot(normals[i], a);
        float t = dist / denom;
        if(denom > 0 && t > tfirst)
        {
            tfirst = t;
        }
        else if(denom < 0 && t < tlast)
        {
            tlast = t;
        }
    }
    return tfirst < tlast ? 1 : 0;
}

const vec3 N = {0,-1,0};
const vec3 S = {0,1,0};
const vec3 W = {-1,0,0};
const vec3 E = {1,0,0};
const vec3 D = {0,0,-1};

int
main(void)
{
    vec3 normals[5];
    vec3 player;
    vec3 *targets;
    int i;
    int j;
    vec3 *buildings;
    vec3 *b;
    int nbuildings = 0;
    int n;
    int m;
    char line[300];
    normals[0] = N;
    normals[1] = S;
    normals[2] = W;
    normals[3] = E;
    normals[4] = D;
    fgets(line, 300, stdin);
    n = atoi(line);
    /*5 sides for each building*/
    buildings = calloc(n * n * 5, sizeof(*buildings));
    b = buildings;
    for(i = 0; i < n; i++)
    {
        char *z;
        fgets(line, 300, stdin);
        for(j = 0; j < n && (z = strtok(j ? NULL : line, " \n")) != NULL; j++)
        {
            vec3 bottom;
            vec3 top;
            if(z[0] == '0') continue;
            nbuildings++;
            bottom.x = j;
            bottom.y = i;
            bottom.z = 0;
            top.x = j + 1;
            top.y = i + 1;
            top.z = atoi(z);
            b[0] = top;
            b[1] = bottom;
            b[2] = top;
            b[3] = bottom;
            b[4] = top;
            b += 5;
        }
    }
    fgets(line, 300, stdin);
    player.x = atof(strtok(line, " "));
    player.y = atof(strtok(NULL, " "));
    player.z = atof(strtok(NULL, " \n"));
    fgets(line, 300, stdin);
    m = atoi(line);
    targets = calloc(m, sizeof(*targets));
    for(i = 0; i < m; i++)
    {
        int hit = 1;
        fgets(line, 300, stdin);
        targets[i].x = atof(strtok(line, " "));
        targets[i].y = atof(strtok(NULL, " "));
        targets[i].z = atof(strtok(NULL, " \n"));
        for(j = 0; j < nbuildings; j++)
        {
            b = &buildings[j * 5];
            if(intersect(player, targets[i], normals, b, 5) == 1)
            {
                hit = 0;
                break;
            }
        }
        printf("%d\n", hit ? 1 : -1);
    }
    free(buildings);
    free(targets);
    return 0;
}
laindir
источник
Пробовал несколько тестовых случаев, и вы прошли все из них. Вот тот идеон, который может использовать любой другой для проверки - ideone.com/MTXpzF
Rainbolt