Знищити їх лазерами


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
  • Гравці не розташовуватимуться на куті, на краю або збоку будівлі
  • Ваша лінія зору на ворога ніколи не буде дотичною до кута, краю чи сторони будівлі
  • Гравець - це не перешкода

Радий побачити це з пісочниці :)
Timtech

7
Якщо я не можу знищити ворога, чи можу я приєднатися до них?
Іван Дворак

@ user80551 Вибачте, мені довелося повернути вашу редакцію до назви, оскільки помилка була навмисною. Google це.
Rainbolt

@Rusher О, вибачте, IDK що
користувач80551

4
Пов'язане: youtube.com/watch?v=NKTpWi5itOM
qwr

Відповіді:


5

Перл, 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.


Чи можете ви трохи пояснити своє рішення? Я ще не перевіряв його, але не схиляв Perl, але деякі коментарі були б непогані.
WorldSEnder

@WorldSEnder, схема алгоритму виглядає наступним чином. Пряма лінія PEз'єднує дві точки в 3-D просторі: "Player" (X1Y1Z1) та "Enemy" (X2Y2Z2). Його проекція на (XY)площину перетинає деякі лінії сітки, тобто цілі числа, x = constабо y = constтакі, як X1 < x < X2або Y1 < y < Y2(якщо тут, наприклад X1 < X2, але це не важливо). Координати x yцих перетинів легко знайти, а отже, і zкоординати точки на PEлінії.
користувач2846289

(продовження) З іншого боку, для будь-яких x yкоординат ми знаємо висоту hбудівлі (швидше, максимальну висоту до 4 будівель, які розділяють x yточку). Ворог може бути застрелений, якщо (і тільки якщо) h < zдля всіх згаданих вище "точок перетину". Реалізація - це основні арифметики, а також кілька трюків з Perl з метою гольфу. Децифрування деталей, як я це робив місяць тому, займе певний час :-).
користувач2846289

Argh, як я бачу, в останній (5-й) редакції є помилка: індекси до елементів @aмасиву в grepвиразі повинні з’являтися в порядку 0,3,0,4,1,5,2замість 3,0,3,1,4,2,5- вибачте.
користувач2846289

Гаразд, краще пізно, ніж ніколи, і закінчити цим усім, тут коментується версія.
користувач2846289

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

Я думаю, це повне недоліків. До речі, я зберігав символи на вкладенні (перший рівень - це один пробіл, другий - одна, потім одна табуляція та пробіл ...). Я сподіваюся, що після цього ця відповідь може вказувати на спосіб зробити це.


Я щойно зрозумів, що зразок введення недійсний, тому що один з ворогів був розташований прямо на землі, що технічно є вершиною будівлі нульової висоти, що я пообіцяв, що це не відбудеться. Ваша заявка проходить виправлений тестовий випадок, але це не вдається - ideone.com/8qn3sv . Чи можете ви перевірити мій тестовий випадок? Можливо, мені щось не вистачає або, можливо, моя система координат незрозуміла.
Rainbolt

Ні, це просто те, що вектор йде прямо через кути ... тепер я знаю, чому ви обіцяли Успіння 6 і 7 :)
WorldSEnder

btw, я виводжу негативний поплавок, але це можна виправити за допомогою двох додаткових знаків ( 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;
}

Спробував кілька тестових випадків, і ти їх пройшов усі. Ось ідеон, який кожен може використовувати для перевірки - ideone.com/MTXpzF
Rainbolt
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.