Показаны сообщения с ярлыком задачи. Показать все сообщения
Показаны сообщения с ярлыком задачи. Показать все сообщения

четверг, 11 ноября 2010 г.

Ну и еще одна практически классическая задача

You are given an array X with n elements. Can you compute in linear time a new array A with n elements, where A[i] is the product of all elements in X, but X[i] is excluded. You cannot use division, you may use additional space


Ну и классическое решение примерно такое:
int* test(int* n,int size)
{
    int* a = new int[size];
    for(int i = 0,temp = 1;i,size;temp*=n[i],++i)
        a[i]=temp;
    for(int i=size-1,temp=1;i>=0;temp*=n[i],--i)
        a[i]*=temp;
    return a;
}


а вот красивое функциональное O(N) решение я придумать не могу...

Еще одна почти стандартная задача

All numbers in an array repeat twice except two numbers which repeat only once. All the numbers are placed randomly. Find out efficiently the two numbers that have not repeated with O(1) extra memory.

O(1) extra memory - значит никаких хэшей.
очевидный ответ - отсортировать массив - слишком очевиден. А поскольку он выполняется за O(NlogN) можно предположить что это достичь результата можно за O(N) как минимум.

среда, 10 ноября 2010 г.

интересная задачка


Given two integer arrays and the size of each array.
Determine if arrays match.
Order of elements does not matter.
Number of occurrences does matter.
Return true for match, false for no match
Computational complexity is important.


Интересна она тем что первые пришедшие в голову решения - сортировка O(NlogN) либо хэш O(N) не оптимальны.

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

понедельник, 1 ноября 2010 г.

пятница, 14 ноября 2008 г.

Из архивов

Нашел тут старую запись.
Суть сводилась к следующей задаче:
Во входном файле две ASCII-строки, одна состоит только из больших латинских букв, в другой могут встречаться большие латинские буквы и еще два спецсимвола - * (звездочка) и ? (знак вопроса). Строки могут иметь длину от 1 до 255 символов, быть в файле в произвольном порядке (но их всего две, формат входных данных корректен). Строку только с буквами назовем словом. Строка со спецсимволами - шаблон, в котором "?" и "*" играют роль символов подстановки по правилам, идентичным wildcards в именах файлов в DOS или Unix-shell, т.е. "?" заменяет собой ровно один произвольный символ, а "*" заменяет собой любое количество произвольных символов - 0 или более (т.е. может заменять и пустую строку). Программа должна выдать ответ YES, если слово подпадает под шаблон (match'ит его), либо NO в противном случае.

Ок. Паттерн-матчинг. Решил "поучаствовать" на время. Заняло минут 15. Получилось вот так вот:

bool match(const char* s,const char* p)

{

    if(!p)return *p==*s;

    if(*p==*s||*p=='?') return match(s+1,p+1);

    while(*s && !match(s++,p+1));

    return *s?true:match(0,p+1);

}

еще минут десять искал ошибку, в результате в код перед while добавилась строчка
if(*p!='*')return false;

красиво (правда проверки на то что строки содержат только большие латинские буквы нет)...можно было бы все лавры себе приписать...если бы не...
Есть такая книжка - Beautiful Code. Одня из статей в ней - как раз и реализация подобного алгоритма на С. Автор статьи - Кернинган, тот самый, брат Ричи. Я кода не помню, но помню одно - было действительно красиво. Красота она в рекурсии... следовательно... см. код. Т.е. алгоритм выстроился в основном по воспоминаниям об этой статье. Даже не столько по воспоминаниям о статье сколько об абстрактных воспоминаниях об ощущениях в процессе цтения. Не читай я эту статью не уверен что до идеи рекурсии дошел бы самостоятельно.
Занятно.

...........
Просмотрел комменты. Наткнулся на:

bool match(const char *word, char const *pattern)

{

    if (!*pattern) return !*word;

    if (*word == *pattern || *pattern == '?') return match(word+1, pattern+1);

    if (*pattern != '*') return false;

    while (*word)  if (match(word++, pattern+1)) return true;

    return match(word, pattern+1);

}


автор кода либо шибко шарит либо тоже читал эту книжку

четверг, 23 октября 2008 г.

Google Treasure Hunt 2008

Смотреть тут...
Вот...хотелось задачки порешать.
После ProjectEuler оказалось - ни о чем. Самое сложное - ждать 10 минут проверки решения...

Разве что задачка про робота неплоха, требует немного подумать, вполне может подойти для интервью.
Вот решение на питоне :

def google_test_robot():

    d={}

    d[(0,0)]=1

    def helper_(a,b):

        if a<0 or b<0:

            return 0;

        if not d.has_key((a,b)):

            d[(a,b)] = helper_(a-1,b)+helper_(a,b-1)

        return d[(a,b)]

    print helper_(41,44)



несмотря на "меморизацию" :) решение далеко не самое оптимальное как с точки зрения используемой памяти так и с точки зрения скорости. Правильнее было бы идти по диагонали...но так писать дольше.
И уж совсем правильнее было бы использовать так называемые Catalan numbers (ну это если повезло и поле квадратное)

среда, 3 сентября 2008 г.

Google Code Gem - round 1A

Первая задача первого раунда была повеселее...

Problem

You are given two vectors v1=(x1,x2,...,xn) and v2=(y1,y2,...,yn). The scalar product of these vectors is a single number, calculated as x1y1+x2y2+...+xnyn.

Suppose you are allowed to permute the coordinates of each vector as you wish. Choose two permutations such that the scalar product of your two new vectors is the smallest possible, and output that minimum scalar product.


Как решал я...
Ну во первых перебор это конечно хорошо для малых n...но поскольку в большем тестовом задании n==800 а 800! это "много" от перебора можно отказаться сразу.

Сначала делаем матрицу n*n, такую что m[i][j]=v1[i]*v2[j];
Обычное скалярное произведение это Cум(m[i][i]). У нас же эта будет такая сумма что i и j встречаются только один раз.

Рассмотрим следующую идею.
Возьмем какое-то скалярное произведение и будем его улучшать до тех пор пока улучшение возможно.
Как улучшать - ищем такие m[i][j] и m[l][k] что что m[i][j]+m[l][k]>m[i][k]+m[l][j]. Как только нашли меняем m[i][j] на m[i][k] и m[l][k] на m[l][j]. Если не смогли найти - ура.

Основной код, очищенный от ввода/вывода:

        vector<pair<int,int>> product;

        for(int i=0;i<a1.size();++i)

            product.push_back(std::make_pair(i,i));

 

        while(true)

        {

            bool improved = false;

            for(vector<pair<int,int>>::iterator v=product.begin();!improved && v!=product.end();++v)

            {

                for(vector<pair<int,int>>::iterator v1=v+1;!improved && v1!=product.end();++v1)

                {

                    int i=v->first;

                    int j=v->second;

                    int l=v1->first;

                    int k=v1->second;

                    if(m[i*a1.size()+j]+m[l*a1.size()+k]>m[i*a1.size()+k]+m[l*a1.size()+j])

                    {

                        v->second = k;

                        v1->second = j;

                        improved = true;

                    }

                }

            }

            if(!improved)

                break;

        }



Остально доказать что такой метод действительно приведет нас к лучшему результату а не просто к неплохому результату...Такие вещи я доказываю очень плохо..но кажется по индукции и это доказуемо.

вторник, 19 августа 2008 г.

Задачка на вероятность.

Задачки на расчет вероятности всегда вызывали у меня определенные трудности.
Но в этот раз вроде удалось обойтись без формул.
Итак
Есть самолет, есть 100 пассажиров. В самолет заходят в порядке очереди, т.е. человек сидящий на 1м месте заходит 1м и т.д. 1й пассажир - неадекват. Заходя в самолет он садится не на свое место а на случайное. Остальные пассажири действуют так - если их место свободно они содятся на свое место. Если занято - на любое.
Вопрос - какова вероятность того что последний 100й пассажир займет свое место.

Ну понятное дело...сначала я пытался формулы выписывать.
Потом в голову пришло решение попроще...
Заметим, что у 100го пассажира не так много вариантов. Он либо займет свое место. Либо он сядет на 1е место. Другой ситуации быть не может. Более того - для любого пассажира если его место занято вероятность занять 1е место (после этого порядок восстановлен) и 100е место одинакова. Для первого пассажира также вероятность занять 1е и 100е место одинаковы. Видно что 1е место и 100е место симметричны. Вывод - 1/2.

четверг, 7 августа 2008 г.

Задачка на переход в двоичную систему исчисления.

Просто часто попадается
есть 1000 бутылок вина. одна отравлена. есть 10 кроликов. любой из них умирает через 15-20 дней после принятия яда. яд смертелен в любых кол-ах. после 21 дня нужно определить, в какая бутылка отравлена


решение - переводим номер бутылки в двоичную систему счисления. Если n-й бит == 1 поим n-го кролика из это бутылки. На 21й день получаем число (если n-й кролик умер на n-й позиции 1 иначе 0, переводим в десятичную).

Про старушек

Из города А в город Б и из города Б в город А на рассвете одновременно вышли две старушки. В 12 часов они встретились. Потом продолжили свой путь. Одна пришла в конечный пункт в 4 часа дня, а другая — в 9 вечера. Вопрос: в каком часу рассвело в этот день?


Задача несложная...но одно но...
По легенде эта задача была задана математику В.Арнольду когда он учился в 5м классе. Соответственно решение должно быть на уровне 5го класса...

Мое решение пятикласника:

Первая старушка двигается за 4 часа прошла столько же сколько вторая от рассвета до обеда. Вторая за 9 часов прошла столько же сколько первыя от рассвета до обеда. тогда t*v1=9*v2 и t*v2=4*v1. Дальше просто но возникает квадратный корень... Не похоже на пятикласника. Проще я придумать не смог, все равно возникает квадратный корень.

вторник, 29 июля 2008 г.

Быстрее быстрой сортировки...

Задача (подсмотрел у avva)

Отсортировать 5 элементов за максимум 7 операций, используя только операции сравнения. Т.е. только ">" или "<".


Поиск решения для меня занял около часа...ибо пошел неправильным путем.

Подсказки к решению:
Лучшее сравнение это такое которое независимо от результата убирает половину возможных вариантов. Если наши сравнения именно такие то за 7 сравнений мы можем найти нужную комбинацию из 2^7=128 возможных комбинаций.

У нас 5 элементов, значит существует 5!=120 возможных комбинаций их взаимного расположения. Значит...

четверг, 17 апреля 2008 г.

Еще интересных задачек. С решениями.

Оригинал.

Есть лошади, которые бегают с разной скоростью и никогда не устают. Лошади могут учавствовать в забегах, в одном забеге может участвовать не более N лошадей. Результатом забега является очередность, в которой лошади дошли до финиша, то есть на выходе - список, на каком месте была какая лошадь в забеге.
Всего лошадей N^2.
Спрашивается - за какое минимальное количество забегов можно найти M лучших их них?

Разбиваем на N групп. В каждой проводим забег. Итого N забегов. Берем из каждой группы победителя. Проводим среди них забег. Победитель есть абсолютный победитель. Запоминаем, победителя удаляем, добавляем второго из его группы. Проводим еще один забег...короче M+N
Есть односвязный список, у которого в каждом элементе есть еще один указатель на некоторый элемент этого списка. То есть, кроме next в ноде есть еще один мембер, который указывает на любой элемент списка. Может сам на себя, могут несколько таких указателей указывать на один элемент, как угодно. Хочется создать копию этого списка, разумеется, со скопированными ссылками в элементах. За линейное время и без дополнительной памяти, кроме как на саму копию.


Код (всякие мелкие очевидные фукнции я убрал, rlist->n - следующий элемент, rlist->r - случайный элемент, предполагается что в исходном списки r!=0 никогда)

rlist* rrcopy(rlist* base_head)

{

    rlist* copy_head = clone_rlist(base_head);

    rlist* b,*c,*t;

 

    for(b=base_head,c=copy_head;b;b=b->n){

        t=c->n;

        c->r=b->r;

        c->n=b->r;

        b->r=c;

        c=t;

    }

 

    for(b=base_head;b;b=b->n){

        c=b->r;

        c->r=c->n->r;

    }

 

    for(b=base_head;b;b=b->n){

        c=b->r;

        t=c->n;

        c->n=b->n?b->n->r:0;

        b->r=t;

    }

    return copy_head;

}

Даны три массива целых положительных чисел длиной N.
Нужно выбрать из каждого из них по одному числу так, чтобы сумма равнялась некоторому данному C.
С константными затратами памяти и сложностью O(N^2).

Хинт - если два массива отсортировать то для двух таких массивов задачу можно решить за O(N). Соответственно вместе с третьим получается O(N^2)
Если массивы сортировать нельзя - не знаю :)

четверг, 10 апреля 2008 г.

Отличная задача на "подумать"

Есть некоторое количество вагонов (конечное), которые сцеплены между собой и образуют неразрывное кольцо. Требуется посчитать вагоны. Вознестись и считать с неба возможности нет. Можно только ходить вдоль состава туда и обратно. В рамках повышения эффективности и человеколюбия счетоводу выдано ведро с краской, которому прилагается кисть. Однако предыдущие счетоводы не справились с задачей, хотя и пытались её решать, поэтому на вагонах могут быть нанесены абсолютно любые знаки – числа, буквы, девичья фамилия вашего дедушки, секретный пароль от вашего журнала, номер вашей зачётки и так далее. Вдобавок, вдоль вагона разбросаны вёдра с краской, аналогичные вашему, и аналогичные кисти. Разбросаны по всякому. Как бы вы ни бросили ведро, какой бы знак ни нарисовали, где бы вы его не поставили, как бы вы его не поставили - всё одно, такое уже могло быть. И вы об этом никак не сможете узнать. А сосчитать вагоны надо.

воскресенье, 16 марта 2008 г.

Project Euler, problem 127

Чем мне нравится Project Euler - практически на любой задаче можно что-то для себя открыть/переоткрыть.

The radical of n, rad(n), is the product of distinct prime factors of n. For example, 504 = 23 × 32 × 7, so rad(504) = 2 × 3 × 7 = 42.
We shall define the triplet of positive integers (a, b, c) to be an abc-hit if:
1. GCD(a, b) = GCD(a, c) = GCD(b, c) = 1
2. a < b
3. a + b = c
4. rad(abc) < c
For example, (5, 27, 32) is an abc-hit, because:
1. GCD(5, 27) = GCD(5, 32) = GCD(27, 32) = 1
2. 5 < 27
3. 5 + 27 = 32
4. rad(4320) = 30 < 32
It turns out that abc-hits are quite rare and there are only thirty-one abc-hits for c < 1000, with ∑c = 12523.
Find ∑c for c < 110000.


После некоторого достаточно бесплодного с точки зрения результата размышления(о котором тоже в принципе интересно было бы написать) я пришел к решению проблемы практически "в лоб" - перебираем все числа a b c такие что a<b,a+b=c и с<110000
и проверяем выполняются ли для них условия 1. и 4.
Код:

    int count = 0;

    ulong64  sum = 0;

    for(int a=1;a<limit ;++a){

        int b = a+1;//a < b

        int c = a+b;//c = a + b

        while(c<limit){

            if( ! (cross(dpf[a],dpf[b]) || cross(dpf[a],dpf[c]) || cross(dpf[b],dpf[c]))){//GCD(a, b) = GCD(a, c) = GCD(b, c) = 1

                if(rad[a]*rad[b]*rad[c]<c_long){//rad(abc) < c

                    ++count;

                    sum+=c;

                }

            }

            c = a+(++b);

        }

    }




Здесь dpf[i] - массив из простых делителей числа i (например для 504 это будет [2,3,7])
rad[i] - произведение все чисел из массива dpf[i].
Функция cross(a,b) возвращает false если массивы a и b не пересекаются, т.е. нет такого числа i входящего одновременно в оба массива.
соответственно
if( ! (cross(dpf[a],dpf[b]) || cross(dpf[a],dpf[c]) || cross(dpf[b],dpf[c]))){
соответствует условию
1. GCD(a, b) = GCD(a, c) = GCD(b, c) = 1
Далее из этого же условия следует что rad(a*b*c) == rad(a)*rad)b)*rad(c), соответственно
if(rad[a]*rad[b]*rad[c]<c_long)
соответствует условию
4.rad(abc) < c

Вообщем надеюсь логика более менее понятна. На тестовом условии (для c<1000 count == 31 и sum == 12523) этот подход отрабатывал. Оставалась мелочь - запустить его же для c<110000.

Первая проблема была - ну оооочень долго выполняется. Когда спустя минут 10 я добрался до a=4000 я прервал выполнение.

Первая попытка оптимизации - если а и b четные их можно не проверять. Поэтому добавилось условие
if(a%2 == 1 || b%2 == 1)

Все равно очень долго...да и явно неправильный результат.
В чем проблема? переполнение... переполнение может происходить при проверке условия rad(a)*rad)b)*rad(c). Переходим к unsigned long long. Результат вроде становится правильным...но все равно ооочень долго.

Хм...хм...
Возможно кому-то решение видно сразу. Я думал достаточно долго. Даже было желание вновь придумать другой подход.
В результате прогнав несколько тестов (на меньшем значении c) я понял в чем дело.
Во внутреннем цикле используются два условия - 1. и 4. Результат должен удовлетворять обеим условиям. С этой точки зрения условия равнозначны. Но - они не равнозначны с точки зрения времени выполнения и частоты срабатывания.
Условие 4. срабатывает реже чем условие 1. И в то же время выполняется намного быстрее.

Поэтому все что нужно - поменять условия 1. и 4. местами...
Результат (я убрал переход к unsigned long long для наглядности):

    int count = 0;

    ulong64  sum = 0;

    for(int a=1;a<limit ;++a){

        int b = a+1;//a < b

        int c = a+b;//c = a + b

        while(c<limit){

            if(a%2 == 1 ||  b%2 == 1){

                if(rad[a]*rad[b]*rad[c]<c_long){//rad(abc) < c

                    if( ! (cross(dpf[a],dpf[b]) || cross(dpf[a],dpf[c]) || cross(dpf[b],dpf[c]))){//GCD(a, b) = GCD(a, c) = GCD(b, c) = 1

                        ++count;

                        sum+=c;

                    }

                }

            }

            c = a+(++b);

        }

    }


Время выполнения стало 29 секунд.

Выводы.
Порядок выполнения проверок имеет значение... Вывод в общем то очевидный. Но про него часто забывают.

Кстати, родилась простая задачка которую можно использовать на собеседованиях.
Есть два отсортированных по возрастанию массива целых чисел. Определить пересекаются ли эти массивы за время O(n+m), где n и m - длина массивов.

понедельник, 3 марта 2008 г.

Project Euler, problem 100

Расту. Быстро набросав brute force переклчился на "листик и ручку". Оказалось - и тут Diophantine Equation

Потом посмотрел что там у меня набрутфорсилось...оказалось таки да, Diophantine Equation.

Дальше все было уже просто.

On-Line Encyclopedia of Integer Sequences - вообще полезный ресурс для этих задач

пятница, 22 февраля 2008 г.

Project Euler, problem 78

4 дня...4 дня...4 дня я изобретал формулу Эйлера, потом переоткрывал пентагональные числа...потом оптимизировал все это...

среда, 20 февраля 2008 г.

Project Euler, problem 117

What I really like about "Project Euler" competition is - sometimes problems that looks very hard have very easy solution. Actually they may be solved using "p&p" (paper and pencil)programing language.

Problem 117 looks beautiful. Its very easy to make complex recursive solution and a bit harder to think and make very simple solution without any recursion.

Here is mine:

LONGLONG euler_117()

{

    map<int,LONGLONG> _set;

    _set[0]=1;_set[1]=1;_set[2]=2;_set[3]=4;

    for(int i=4;i<=50;++i)

        _set[i]=_set[i-1]+_set[i-2]+_set[i-3]+_set[i-4];

 

    return _set[50];

}

суббота, 9 февраля 2008 г.

Project Euler Problem 145 - slap in the face...

Some positive integers n have the property that the sum [ n + reverse(n) ] consists entirely of odd (decimal) digits. For instance, 36 + 63 = 99 and 409 + 904 = 1313. We will call such numbers reversible; so 36, 63, 409, and 904 are reversible. Leading zeroes are not allowed in either n or reverse(n).

There are 120 reversible numbers below one-thousand.

How many reversible numbers are there below one-billion (10^9)?


Sounds simple? Sure...brute force it. Just like I do. Then go check the other solutions and feel yourself stupid bruteforcer...

This problem can be solved analytically.

пятница, 8 февраля 2008 г.

Find if digits in two numbers are permutations of each other

I was working on Project Euler problem 72

One subtask was to create function to check where numbers "a" and "b" are permutations of each other...
When I finish this problem and check other solutions on forum...I found few C++ solutions. The realization of this function was awful..and extremely slow.

Here is mine:

bool is_permutation(int a,int b)

{

    char test1[10]={0};

    char test2[10]={0};

    while(a>0 && b>0)

    {

        ++test1[a%10];

        ++test2[b%10];

        a/=10;b/=10;

    }

    if(a!=b)

        return false;

    return memcmp(test1,test2,10)==0?true:false;

}



There was one really close realization...
(you can ++test1[a%10];--test2[b%10]; and at the end check if test1=={0} )

But the others...

1st (O(n^2) on sorting numbers... cmon man can't you just qsort then):

#define CAP_LENGTH 8

inline int permutation(int a, int b){

    static int a_digits[CAP_LENGTH];

    static int b_digits[CAP_LENGTH];

    static int i,j, a_length, b_length;

    i=0;

    do{

        a_digits[i]=a%10;

        i++;

    } while( (a/=10)!=0 );

    a_length=i;

    i=0;

    do{

        b_digits[i]=b%10;

        i++;

    } while( (b/=10)!=0 );

    if(a_length!=i)

        return 0; //even digit number differ between a and b

    b_length=i;

    for(i=0; i<a_length; i++){

        // cycle on all a digits

        for (j=0; j<b_length; j++){

            // cycle on all the (remaining, valid) b digits

            if(a_digits[i]==b_digits[j]){

                b_digits[j]=-1;

                break;

            }

        }

        if(j==b_length)

            return 0;

    }

    return 1;

}



2nd (gogo STL...):

bool are_permutations (int num1, int num2)

{

    /* Must have the same number of digits. */

    if ((int) log10 (num1) != (int) log10 (num2))

        return false;

 

    std::deque<char> digits1, digits2;

    int sorted1 = 0, sorted2 = 0;

 

    while (num1 > 0)

    {

        digits1.push_back (num1 % 10);

        num1 *= 0.1;

    }

 

    while (num2 > 0)

    {

        digits2.push_back (num2 % 10);

        num2 *= 0.1;

    }

 

    std::sort (digits1.begin (), digits1.end ());

    std::sort (digits2.begin (), digits2.end ());

 

    for (std::deque<char>::iterator i = digits1.begin (); i != digits1.end ();

        ++i)

        sorted1 = sorted1 * 10 + *i;

 

    for (std::deque<char>::iterator i = digits2.begin (); i != digits2.end ();

        ++i)

        sorted2 = sorted2 * 10 + *i;

 

    return sorted1 == sorted2;

}



3rd (and the winner is...sprintf, qsort,strlen...):

bool perm(int a, int b)

{

    static char b1[1024];

    static char b2[1024];

 

    sprintf(b1, "%d", a);

    sprintf(b2, "%d", b);

 

    int l1 = strlen(b1);

    int l2 = strlen(b2);

 

    if (l1 != l2)

        return false;

 

    qsort(b1, l1, 1, cmp);

    qsort(b2, l2, 1, cmp);

 

    return !strcmp(b1, b2);

}