Как стать автором
Обновить
0
0

Пользователь

Отправить сообщение
Задача 1. Легкотня
public class Node {
    public String value = null;
    public Node next = null;

    public Node(String _value, Node _next) {
        value = _value;
        next = _next;
    }
    
    public static Node reverse(Node _node){
        Node base = _node;
        Node result = null;     
        while(base!=null){
            Node temp = base;
            base = temp.next;
            temp.next = result;
            result = temp;
            
        }
        return result;
    }
}


Задача 2. Без кода.
По сути сводится к нахождению для каждого элемента убывающей последовательности слева и справа, и суммированию длин этих последовательностей. Правда решается за квадратичные затраты от n. ХМ, э нет, я пожалуй поищу решение получше.

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

Вопрос 2
Нужно разделить слиток на 3 части в пропорции 1-2-4.
В первый день отдает 1.
Во второй день забираем 1 обратно и отдаем 2.
в среду отдает 1 и ничего не берем в замен.
в четверг отдает 4 и забираем обратно 1 и 2
и т.д.

Задача 3
Пока без кода. Если воспринимать условия, как «в любой момент должна сохраняться последовательность четных и нечетных», то задача может решится только за O(n^2).
В этом случае, замену местами можно сделать только если 2 элемента стоят рядом. А это квадратичная зависимость.
Если же последовательность не обязана сохраняться, а должна быть только восстановлена в конце, то я предлагаю такой ход мыслей.
У нас есть фиксированный набор переменных, можно ограничится всего 1. Это значит, что элементы в массиве можно ТОЛЬКО менять местами.
Нам нужна линейная зависимость. Вспоминаем, что сумма убывающей геометрической прогрессии линейна. n+1/2n+1/4n+1/8n+...<2n.
Делим наш массив на 3 зоны, в пропорции 1-2-1. Добиваемся, что все числа из первой и третей зоны стали на место, а вторая часть опять заполнена в перемешку. Таким образом мы за 1 итерацию свели задачу с ней самой же, но сократили длину в 2 раза.
Т.е. если у нас есть 1a2b3c4d5e6f, то после первой итерации получим 123-4a5b6c-def.
Здесь есть вопросы нечетной длины, и теоретического обоснования, но суть концепции представлена. Постараюсь оформить в виде кода.
Код для задачи 3
    public static float waterFall(int _position, float _water){
        int level = getLevelPosition(_position);
        int firstCup = (level-1)*level/2+1;
        int numberAtLevel = _position-firstCup+1;
        if(numberAtLevel*2>level){
            numberAtLevel = level - numberAtLevel+1;
        }
        int steps = level - numberAtLevel+1;
        float[] array = new float[numberAtLevel];
        array[0]= _water;
        for(int i=0; i<steps; i++){
            if(i!=0){
                array[0]=Math.max(0f, (array[0]-1f)/2f);
                System.out.print(" 0 = "+array[0]);
            }
            for(int k =1; k<numberAtLevel; k++){
                array[k]=Math.max(0f, (array[k-1]-1f)/2f)+Math.max(0f, (array[k]-1f)/2f);
                System.out.print(" | * = "+array[k]);
            }
            if(array[numberAtLevel-1]<=0f){
                return 0f;
            }
            System.out.println();
        }
        return Math.min(1f, array[numberAtLevel-1]);
    }
    
    public static int getLevelPosition(int _position){
        int level = (int)Math.round((-1+Math.sqrt(1+8*_position))/2.f);
        int lastCup = (level+1)*level/2;
        if(_position<=lastCup){
            return level;
        }else{
            return level+1;
        }
    }
    
    public static void main(String[] _a){
        float b = waterFall(8, 8f);
        System.out.println(" "+b);
    }

Оставил вывод поэтапной информации, чтобы легче было отследить правильность.
На этой неделе я пас.
По всей видимости вы правы. Много раз встречался с похожей задачей на практике, но прикидывал примерное решение, ничего не находил и старался обойти этот момент. Т.е. Задача не выдуманная, а вполне реальная.
Однозначно ей делать нечего на собеседовании из-за её сложности. Я так понимаю, что все «быстрые» методы не дают гарантированное решение.
По первой задаче.
Здесь можно привести аналогию с поиском минимума на 2d карте. Есть такой алгоритм — движение в направлении градиента. Но вот проблема, вы можете попасть в ловушку ЛОКАЛЬНОГО минимума, и не сможете дойти до абсолютного.
Например у вас есть такая разбивка:
1000 1100 (+100)
2111 2000 (-111)
3005 3000 (-5)
4404 4000 (-404)
5000 5200 (+200)
6000 6200 (+200)
— (-20)
Я специально взял такие цифры, чтобы замена чисел разными строчками давали большую разницу. По вашей логике мы находим в таблице переходов найти число, близкое к -20. Очевидно, что это -5 (3-я строка). После обмена значениями в 3-1 строке, разница станет -10, и больше вы ничего заменить не сможете, т.к. все остальные числа в таблице переходов гораздо большие, чем -10 по модулю.
Но если вместо 3-й строки провести обмен в 1 и 2 строках сразу, то в итоге мы получим разницу всего в +2.
Поэтому утверждение «гарантировано каждый раз разница уменьшается» не приводит к нахождению минимума.
Поэтому пока остается полный перебор. Это означает С(n, n/2)=n!/((n/2)!*(n/2)!).
При n=60 получаем примерно 10^17, а при n=100 уже примерно 10^23. Это при том, что мы увеличили значение меньше, чем в 2 раза (100 против 60) результат возрос очень сильно.
Ради интереса, сколько будет работать ваш Кот при длине массива всего в 60? Сгенерируйте такой массив из 60 элементов например от 1000 до 9999.
Традиционно мои варианты:
Вопрос 1
При использовании 2 костей, всего граней 12, причем на обоих костях должны быть грани 0 1 2. Осталось по 3 грани на каждой кости, их заполняем 3 4 5 и 6 7 8. Не хватает грани для 9. Но если перевернуть 6, то можно юзать её как 9. Это не работает на некоторых шрифтах, но этим мы пренебрежем.

Вопрос 2. Не понял условия. Что можно делать с монетами, точнее что может дать нам ощупывание монет? Попробовал на ощупь сравнить 2 монеты — не получается.
Задача 1. Не решена. Не вижу способа, кроме полного перебора, но там сложность порядка n! Такие решение рассматривать не имеет смысла.
Задача 2
public static int maxSum(int[] _array){
        int sum_0 =0;
        int sum_1 =0;
        for(int i=0; i<_array.length; i++){
            int value = _array[i];
            int sum_0_next = sum_1;
            sum_1 = Math.max(sum_1, sum_0+value);
            sum_0 = sum_0_next;
        }
        return sum_1;
    }
    
    public static void main(String[] _ar){
        int[] mayarray = {15,1,2,3,4,15,6};
        System.out.println(maxSum(mayarray));
    }

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

Задача 3
Пока без кода. По номеру рюмки мы должны вычислить ряд в котором она стоит и номер в этом ряду. Далее выделяем «ромб» (ну как бы ромб, параллелограмм) верхняя вершина совпадает с первой рюмкой, нижняя с целевой чашкой, а грани идут параллельно боковым сторонам треугольника. Этот «ромб» охватывает все кружки, которые имеет смысл вычислять.
Пусть имеем «ромб» со сторонами 7 и 3. Выбираем меньшее 3. Выделяем массив размером с 3. Вычисляем не сложными действиями количество воды прошедшие через кружки (w, (w-1)/2, ((w-1)/2-1)/2). Потом в цикле (всего 7 итераций или точнее 6=7-1) сдвигаем ряд по второй стороне, и вычисляем кол-во воды в кружках, так мы доходим до целевой кружки.
Если воды там больше чем 1, то возвращаем 1.
Мы имеем 3 указателя. index1 -движение слева направо, index2 -движение справа налево (самый правый элемент, который !=2), index0- самый левый элемент, который не равен 0.
Пример. 0001102122 (жирным выделены указатели)
Смотрим на что указывает указатель index1, в примере это 0.
Если это ноль, то меняем значение index0 и index1 местами, и сдвигаем оба индекса на 1. Резальтат: 0000112122
Если же index1 указывает на 1, то просто сдвигаем его влево на 1, если указывает на 2, то меняем местами значения в ячейках index1 и index2, и уменьшаем index2 на 1.
Вот рабочий код, немного запутанный, после каждого обновления индекса приходится делать проверку, чтобы не выйти за приделы массива.
public static void sort012(int[] _array){
        int index0 = 0;
        int index1 = 0;
        int index2 = _array.length-1;
        int val1 = _array[index1];
        int val2 = _array[index2];
        
        while((val1==0)&&(index1<index2)){
            index1++;
            val1 = _array[index1];
        }
        while((val2==2)&&(index1<index2)){
            index2--;
            val2 = _array[index2];
        }
        index0 = index1;
        if(index1>=index2){
            return;
        }
        // 
        while(true){
            if(val1==0){
                if(index1!=index0){
                    _array[index0] = 0;
                    _array[index1] = 1;
                }
                index0++;
                index1++;
                if(index1>index2){
                    return;
                }
                val1 = _array[index1];
                continue;
            }
            if(val1==2){
                _array[index2] = val1;// =2
                _array[index1] = val2;
                val1 = val2;
                index2--;
                if(index1>index2){
                    return;
                }
                val2 = _array[index2];
                while(val2==2){
                    index2--;
                    if(index1>index2){
                        return;
                    }
                    val2 = _array[index2];
                }
                continue;
            }
            while(val1==1){
                index1++;
                if(index1>index2){
                    return;
                }
                val1 = _array[index1];
            }
        }
    }
    
    public static void main(String[] _a){
        //int[] ar = {0,1,2,0,1,2,0,0,2,2,1,2,0,1,1,0,2,2};
        int[] ar = {2,1,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0};
        sort012(ar);
        for(int i:ar){
            System.out.println(i);
        }
    }

Здесь каждый элемент массива читается 1 раз. Правда каждый элемент массива может быть изменён 2 раза. Поэтому это скорее полтора прохода.
Мы можем сортировать его по ходу, двигаясь слева и справа, и проводить сортировку тех элементов, которые были пройдены.
Поэтому сортировать именно существующий массив.
Прошу прощения, что долго не отвечал. Не было времени.
Там конечно получается не чистый 1 проход, скорее полтора прохода.
Чистый 1 проход получился бы если в задаче были только 0 и 1.
public static void sort01(int[] _array){
        int leftIndex =0;
        int rightIndex = _array.length-1;
        while(true){
            while(_array[leftIndex]==0){
                leftIndex++;
            }
            while(_array[rightIndex]==1){
                rightIndex--;
            }
            if(leftIndex>=rightIndex){
                return;
            }
            _array[leftIndex]=0;
            _array[rightIndex]=1;
            leftIndex++;
            rightIndex--;
        }
    }

Правда у такого варианта есть косяк: если массив нулевой длинный, или же если там одни 0 или 1, то выход за пределы массивы. Но это можно пофиксить либо добавив по 1 проверке в цикле, либо добавить (и запомнить) единицу и ноль в массив, а после уже вернуть в начальное значение.
А так 1 проход: каждое значение (индекс) читается не больше 1 раза, и записывается не более 1 раза, за исключение чтения элементов «при встрече» индексов.
Задача с 0,1,2 немного сложнее, но думаю вам самим будет интересно решить её по аналогии.
Таких легких задач еще не было.
Вопрос 1. Такие вопросы мы решали в 5 классе.
Вопрос 2. Такой вопрос был мне задан в ЕПАМе. Самые тормознутые должны идти вместе.
Задача 1. Если не применять «избыточную» оптимизацию, то легко решается в 2 прохода, считаем кол-во 0,1,2 и за второй проход расставляем по местам. Но можно решать за 1 проход, но сильно это ничего не ускорит, а вот код окажется намного сложней.
Задача 2. Такая задача была в одной из книг Шилдта. Я её сам не решил. Но ответ знаю.
Задача 3. Если я все правильно понял, то при выборе 3-ки во втором примере мы потеряем всего одну двойку и 1 четверку, а не все двойки. Т.е. удаляются не все элементы равные значению, а только один из них. Если так, что решение — каждый раз выбирать наибольший из оставшихся элементов.
Из текста автора мы не можем оценить полезность рефакторинга. Вполне может быть, что без него в том случае было ну никак. Может там было +200%, а может и +5%.
Напомнило:
Если вы такие умные, почему строем не ходите?
Помните выражение «все готовы снимать яблоки, но никто не садит деревья»?
Очень могу себе представить, что деньги в скрытом виде приносят как раз наивные программисты, которые не понимают «как работает бизнес». А вот всякие прошаренные работники делают метрики (вид, что приносят пользу). И этим вводят в заблуждение директора, который думает, что «видит всю деятельность фирмы».
Сделал доки — снизил затраты на поддержание продукта скажем на 30% (если не больше). Просто эти затраты не короткие (будут видны не сразу), быстро их не увидишь, не почувствуешь.
Человек 8 часов спит, 8 часов личного времени, 8 часов работает. 8 часов сна не приносят ни прибыли, ни счастья, давайте откажемся от сна. К чему это приведет?
Выпуск не понравился, половину заданий я не понял.
1 Вопрос
В чем суть вопроса? В том, чтобы добавить ещё одну таблетку для равновесия, и потом разделить коктель на 2 дозы вертикальной перегородкой (лекарства могут быть слоями с разными плотностями)

Вопрос 2
Не понятно зачем, и не понятно что можно делать. Для себя я понял вопрос так: нужно узнать среднюю, но чтобы никто не узнал ЗП другого работника. Первый человек говорит второму случайное число(и запоминает его). Второй прибавляет свою ЗП к числу и говорит сумму третьему. Третий также прибавляет свою ЗП и говорит первому. Первый с данному значению прибавляет свою ЗП и отнимает запомненное число. Делит на 3 и говорит всем среднюю ЗП.

Задача 1
2^(n-1). Почему так? Возмем число 7, разобьем его на семь единиц. 7 = 1 1 1 1 1 1 1. Между 7-ю единицами есть 6 промежутков. В каждом из промежутков может либо быть "+", либо не быть. Единицы, между которыми нет плюса объединяем в одно число. 7=11+1+1+111=2+1+1+3. Каждая комбинация плюсов дает 1 последовательность, и каждая последовательность дает ровно 1 комбинацию. Поэтому число их вариантов одинаково.

Задача 2
Логично такую задачу решать рекурсией. Но рекурсия обрабатывает разряды со старшего, при этом, если в старших разрядах есть 3, то от младших ничего не зависит. т.е. 35 и 38 дадут одинаковое кол-во. Поэтому, если найдена 3, то делаем число отрицательным и передаем его вверх. Не сложно заметить, что 10 — это 9 комбинаций, 100-это 9*9=81 комбинаций, 1000-это 9*9*9 комбинаций.
Код не причесан, но решение должно быть понятно.
public static boolean isRight(int _i){
        int h = _i;
        while(h>0){
            int hh = h/10;
            if(h-hh*10==3){
                return false;
            }
            h = hh;
        }
        return true;
    }
    
    public static void main(String[] _a){
        for(int i=1; i<100000; i++){
            int z1 = bruteforce(i);
            int z2 = Math.abs(shum(i, 1));
            if(z1!=z2){
                System.out.println(" "+i+"   "+z1+"   "+z2);
            }
        }
    }
    
    public static int bruteforce(int _h){
        int s = 0;
        for(int i=1; i<=_h; i++){
            if(isRight(i)){
                s++;
            }
        }
        return s;
    }
    
    public static int shum(int _h, int _power){
        if(_h==0){return 0;}
        int g = _h/10;
        int c = _h-g*10;
        int d = shum(g, 9*_power);
        if(d<0){return d;}
        if(c<3){return d+c*_power;}
        if(c>3){return d+(c-1)*_power;}
        return (shum(g, 9*_power)+c*_power-1)*-1;
    }


В коде идет проверка моего способа с полным перебором.

Задачу 3 не понял.
Решение задачи 1. Пока без кода
Можно отсортировать весь массив и взять центральный элемент. Это означает r*c*Log(r*c).
Можно постараться найти решение за r*c. Но r*c — это предел для данных без структуры. У нас же есть порядок по строкам, поэтому можно искать решения вида r*Log( c), или что-то типа того.
Если коротко, как в бинарном поиске храним верхнюю и нижнюю границы по значению. Кроме того, храним правую и левую границы для каждой строки. Каждый раз берем медиану по каждой строке (с учетом границ), находим среди этих медиан новую медиану и пробуем её, как искомое значение. Находим для каждой строчки положение этого значения бинарным поиском (с учетом границ). Для левой границы ищем положение значения меньше или равно, а для правой больше или равно. Если сумма по каждой строке больше половины всех элементов массива с обоих сторон, значит значение и есть искомое. Если нет, то в каждой строке передвигаем границу.
Нам нужно, чтобы за каждую итерацию отсеивать некий процент (у меня он =25%) оставшихся элементов. Поэтому медиану медиан нужно искать с учётом весов, где вес медианы равен кол-ву оставшихся значений в строке.
Т.е. пусть у нас остались следующие строки (16) (11, 15, 20) (17, 21,33,48,50,77,78) и соотвественно медианы 16, 15, 48 с весами 1-3-7. Если мы возмем просто медиану медиан, т.е. число 16, то отсеем малое значение элементов, т.к. оно произойдет в строках с малым количеством. Это нам не нужно. с другой стороны 3+1+7=11, и медиана приходится на значение 7, т.е. медиану, которая равна 48.
Поиск такой медианы можно делать через сортировку медиан с последующим проходом.
Сложность поиска такой медианы с весом равна r*Log( r). Внутри строк нужен бинарный поиск, и так как зона бинарного поиска сокращается на 25%, то Log( c)+Log(0.75*c)+Log(0.75*0.75*c)+… = O(Log( c)*Log( c)), как сумма арифметической прогрессии.
Итого, сложность всего алгоритма это O(r*Log( r)*Log( c)^2)
Задачи понравились.
Мои решения:
1 Вопрос
При всех вариантах сохраняется четность черных шаров. Значит, черных шаров не может из 13 стать 0. Последним будет черный шар.

Вопрос 2
78. Так, как n%5=3 и значение четное, то остается 58, 68, 78, 88, 98. Из них только 78 делится на 3 без остатка.

Задача 3
    public static int nearMix(int[] _array, int _k){
        int s =0;
        for(int i: _array){
            if(i<=_k){
                s++;
            }
        }
        int itemIntoWindow = 0;
        
        for(int i=0; i<s; i++){
            if(_array[i]>_k){
                itemIntoWindow++;
            }
        }
        int minOther = itemIntoWindow;
        for(int i=s; s<_array.length; i++){
            if(_array[i]>_k){
                itemIntoWindow++;
            }
            if(_array[i-s]>_k){
                itemIntoWindow--;
            }
            if(itemIntoWindow<minOther){
                minOther = itemIntoWindow;
            }
        }
        return minOther;
    }
    
    public static void main(String[] _a){
        int array[] = {1,4,3,7,8};
        System.out.println(nearMix(array,3));
    }

Считает количество нужных значений в массиве. За второй раз сканируем окном массив, и считаем количество значений, которые больше k. Минимум этого значения и будет равен кол-ву перестановок.


Задача 2
Пусть у нас есть цифра 9. Выражение длиной в 1 цифру может быть только одно, это сама цифра (первый набор). Выражений из 2-х цифр 9 может быть 4. Запомним все числа, которые при этом получились(второй набор). Чтобы найти все значения, которые состоят из трех цифр нужно взять все числа из первого набора и применить все операции по отношению ко всем числами из второго набора, а также все числа из второго набора на все числа из первого набора. Каждое вновь найденное значение проверяем в хеше, если его нет, то вносим туда(в виде ключа). По этому ключу ложим те значения из которых это число получилось в виде кода «первое число + код операции».
Таким образом пробегаем, пока не заполняем все 10 наборов. А дальше смотрим из Хэша как искомое число получилось и рекурсивно повторяем весь процесс, пока не дойдем до самой цифры.
    public static String StringOfOperations(int _n, int _d){
        if(_n==_d){return ""+_d;}
        Object[] arrayInt = new Object[11];
        HashMap<Integer,Integer> common = new HashMap<>();
        Integer[] first = new Integer[1];
        first[0]=_d;
        arrayInt[1]=first;
        common.put(_d, 0);
        for(int i=2; i<=10; i++){  
            System.out.println("Stage: "+i);
            HashSet<Integer> set = new HashSet<>();
            for(int j=1; j<i; j++){
                Integer[] list1 = (Integer[])arrayInt[j];
                Integer[] list2 = (Integer[])arrayInt[i-j];
                for(int a: list1){
                    for(int b: list2){
                        int c = a+b;
                        if(common.containsKey(c)!=true){
                            common.put(c, a*4+0);
                            set.add(c);
                        }
                        c = a-b;
                        if(common.containsKey(c)!=true){
                            common.put(c, a*4+1);
                            set.add(c);
                        }
                        c = a*b;
                        if(common.containsKey(c)!=true){
                            common.put(c, a*4+2);
                            set.add(c);
                        }
                        if(b==0){
                            continue;
                        }
                        c = a/b;
                        if((common.containsKey(c)!=true)&&(b*c==a)){
                            common.put(c, a*4+3);
                            set.add(c);
                        }
                    }
                } 
            }
            Integer[] next = new Integer[set.size()];
            next = set.toArray(next);
            arrayInt[i] = next;
            set.clear();
        }
        if(common.containsKey(_n)){
            int g = common.get(_n);
            return subPrint(_n, common, _d);
        }
        return "Error";
    }
    
    public static String subPrint(int _value, HashMap<Integer,Integer> _common, int _base){
        if(_value==_base){
            return String.valueOf(_base);
        }
        int a = _common.get(_value);
        int b = a>>2;
        int c = a&0x3;
        int d = 0;
        char operation = 0;
        switch(c){
            case 0:
                operation = '+';
                d = _value - b;
                break;
            case 1:
                operation = '-';
                d =  b - _value;
                break;
            case 2:
                operation = '*';
                d = _value / b;
                break;
            case 3:
                operation = '/';
                d = b/_value;
                break;
        }
        return "("+subPrint(b, _common, _base)+operation+subPrint(d, _common, _base)+")";
    }
    
    public static void main(String[] _a){
        System.out.print(StringOfOperations(200, 9));
    }

Данный код подразумевает, что скобки не влияют на длину значения. И ещё, считаем, что во время выполнения операций полученные значения всегда целые. Доказать это не могу, но думаю, что если в середине получено дробное число, то существует другая комбинация с длинной не большей, чем исходная комбинация.


Задачу 1 ещё обдумываю.
Заголовок спойлера
1 вопрос. Открытыми будут только двери, чьи номера образуют квадрат, т.е. 1,4,9,16,25,...100
2 вопрос. Баян, и вообще вопрос многими считается не корректным.
1. Задача. Отнимаем от числа 1, и побитово умножаем с исходным числом.
2. Задача. Опять повороты массивов. Смысл давать похожие задачи между 2-мя выпусками?
Над последней задачей ещё думаю.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность