понеделник, 24 юни 2013 г.

C# програмиране - част I - Problem 5 – Neuron Mapping - Изпит на 23.06.2013

Задача 1
Задача 2
Задача 3
Задача 4
Задача 5

Здравейте,

Ето че стигнахме до битовата задача.

Условието на задачата може да изтеглите от тук: 5. Neurons.doc

Тази задача я правих 2 пъти from scratch.
Питате се защо ли? Ами причината ще я разберете по-надолу.

Първо ми хрумна следния алгоритъм:
Обръщам всички 0 на 1 и обратно. След което изчиствам рекурсивно всички 1 които се допират до стените, техните съседи и така докато стигна до 0 (достигнатите нули са точно ограждането на някой neuron, които от 1 са станали 0 при обръщането, и по този начин вътрешните не ги докосвам.
Логиката е чудесна и работи за примерите.
Ето и кода:
using System;
using System.Collections.Generic;

class NeuronMapping
{
    static void ClearAtPosition(List input, int x, int y)
    {
        if (x >= 0 && x < 32 && y >= 0 && y < input.Count)
        {
            if ((input[y] & (1 << x)) != 0)
            {
                input[y] = input[y] & (~((uint)1 << x));
                ClearAtPosition(input, x - 1, y);
                ClearAtPosition(input, x + 1, y);
                ClearAtPosition(input, x, y - 1);
                ClearAtPosition(input, x, y + 1);
            }
        }
    }

    static void Main()
    {
        List input = new List();
        while (true)
        {
            string inString = Console.ReadLine();
            if (inString == "-1")
            {
                break;
            }
            uint number = uint.Parse(inString);
            input.Add(~number);
        }
        for (int x = 0; x < 32; x++)
        {
            ClearAtPosition(input, x, 0);
            ClearAtPosition(input, x, input.Count - 1);
        }
        for (int y = 0; y < input.Count; y++)
        {
            ClearAtPosition(input, 0, y);
            ClearAtPosition(input, 31, y);
        }
        foreach (var item in input)
        {
            Console.WriteLine(item);
        }
    }
}

Но се оказа че не покрива 100% от тестовете в bgcoder. Един от тях даваше грешен отговор. От тук на сетне започна голямото чудене зато е така.
Чудех се можеше ли да остане поле между neuron-ите което да не изчиствам. Какви ли не мисли ми минаха през главата.

Накрая реших да започна отначало цялата програма с нов алгоритъм. А именно, за всеки ред първо обръщам с побитова операция НЕ (~), след това от двата края всички единици ги правя от 1 на 0 до срещане на 0.
Този алгоритъм ми докара 100%.

Ето и кода:
using System;
using System.Collections.Generic;

class NeuronMapping2
{
    static void Main()
    {
        List input = new List();
        while (true)
        {
            string inString = Console.ReadLine();
            if (inString == "-1")
            {
                break;
            }
            uint number = uint.Parse(inString);
            number = ~number;
            for (int i = 0; i < 32; i++)
            {
                if ((number & (1 << i)) != 0)
                {
                    number = number & (~((uint)1 << i));
                }
                else
                {
                    break;
                }
            }
            for (int i = 31; i >= 0; i--)
            {
                if ((number & (1 << i)) != 0)
                {
                    number = number & (~((uint)1 << i));
                }
                else
                {
                    break;
                }
            }
            input.Add(number);
        }
        foreach (var item in input)
        {
            Console.WriteLine(item);
        }
    }
}

На пръв поглед ползването на "сложната" рекурсия може да е причината. Но аз не мисля така. Във втория алгоритъм има логически бъг въпреки че ми прави 100%.
Нека да разгледаме следния пример:
................................ 0
.....................11....11... 1560
....................1..1..1..1.. 2340
...................1....11....1. 4290
....................1........1.. 2052
.....................1......1... 1032
......................1....1.... 528
.......................1..1..... 288
........................11...... 192
................................ 0
Този вход би трябвало да ми даде следния изход, ако правилно съм разбрал условието (не го твърдя, нямам претенции английския да ми е перфектен):
................................ 0
................................ 0
.....................11....11... 1560
....................1111..1111.. 3900
.....................11111111... 2040
......................111111.... 1008
.......................1111..... 480
........................11...... 192
................................ 0
................................ 0
И точно това е резултата с първия алгоритъм.
А със втория резултата е:
................................ 0
.......................1111..... 480
.....................11.11.11... 1752
....................1111..1111.. 3900
.....................11111111... 2040
......................111111.... 1008
.......................1111..... 480
........................11...... 192
................................ 0
................................ 0
Но пък той ми донесе 100%


Хареса ми преживяването имаше емоция, трудности, проблеми за решаване. Удоволствието е невероятно, ако се сблъскаш с трудности и накрая успееш да се справиш.

И благодаря за времето което отделихте да прочетете преживяванията ми по време на изпита.

C# програмиране - част I - Problem 4 – Fire in the Matrix - Изпит на 23.06.2013

Задача 1
Задача 2
Задача 3
Задача 4
Задача 5

Здравейте,

Тези задачи с чертането са ми били винаги забавни.
Ето и впечатленията ми.

Условието на задачата може да изтеглите от тук: 4. Fire.doc

Когато имаме такива задачи предпочитам следния подход: Използвам 2 вложени цикъла за обхождане на правоъгълника в който изчертаваме от вида:
for (int y = 0; y < ySize; y++)
{
    for (int x = 0; x < xSize; x++)
    {
        ...
    }
    ...
}

Координатната система е малко различна от математическата, и разликата е в това че Y координатата се увеличава надолу за разлика от математиката, където е нагоре.
След това използвам един if за проверка на математическа формула на графика и се получава точното изчертаване. Например ако искаме да изчертаем:
x...
.x..
..x.
...x
Използвам следния код:
for (int y = 0; y < 4; y++)
{
    for (int x = 0; x < 4; x++)
    {
        if(x == y)
        {
            Console.Write("x");
        }
        else
        {
            Console.Write(".");
        }
    }
    Console.WriteLine();
}
Аналогично за:
...x
..x.
.x..
x...
Се получава с:
for (int y = 0; y < 4; y++)
{
    for (int x = 0; x < 4; x++)
    {
        if(x + y == 4 - 1)
        {
            Console.Write("x");
        }
        else
        {
            Console.Write(".");
        }
    }
    Console.WriteLine();
}
Умишлено написах 4 - 1 в проверката, понеже при динамични размери се получава точно по тази формула.
Горните 2 варианта се използват за чертане на прави или част от прави, и ги използвах при пламъците.

Има възможност да се изчертае и цяла област в страни от права. Например за:
...x
..xx
.xxx
xxxx
Се използва следната проверка:
for (int y = 0; y < 4; y++)
{
    for (int x = 0; x < 4; x++)
    {
        if(x + y >= 4 - 1)
        {
            Console.Write("x");
        }
        else
        {
            Console.Write(".");
        }
    }
    Console.WriteLine();
}
Този подход използвах при дръжката, като я разделих визуално на ляв правоъгълник и десен.
Където ключовите проверки са x < half и x >= half.

В този дух използвайки координатна система и формули си нарисувах факлата на 3 части.
1. Огъня горната му част.
2. Огъня долната част.
3. Линията с тиретата (тук използвах създаване на string от char и брой.
4. Дръжката

Ето и кода:
using System;

class FireInTheMatrix
{
    static void Main()
    {
        int n = int.Parse(Console.ReadLine());
        int half = n / 2;
        int quoter = n / 4;
        for (int y = 0; y < half; y++)
        {
            for (int x = 0; x < n; x++)
            {
                if ((x + y == half - 1) ||
                    (x - half == y))
                {
                    Console.Write("#");
                }
                else
                {
                    Console.Write(".");
                }
            }
            Console.WriteLine();
        }
        for (int y = 0; y < quoter; y++)
        {
            for (int x = 0; x < n; x++)
            {
                if ((x == y) ||
                    (x + y == n - 1))
                {
                    Console.Write("#");
                }
                else
                {
                    Console.Write(".");
                }
            }
            Console.WriteLine();
        }
        Console.WriteLine(new string('-', n));
        for (int y = 0; y < half; y++)
        {
            for (int x = 0; x < n; x++)
            {
                if (x >= y && x < half)
                {
                    Console.Write("\\");
                }
                else if (x + y < n && x >= half)
                {
                    Console.Write("/");
                }
                else
                {
                    Console.Write(".");
                }
            }
            Console.WriteLine();
        }
    }
}


C# програмиране - част I - Problem 3 – Bulls and Cows - Изпит на 23.06.2013

Задача 1
Задача 2
Задача 3
Задача 4
Задача 5

Здравейте,
Благодаря за търпението и че сте стигнали до 3-та задача.
Ето и впечатленията ми.

Условието на задачата може да изтеглите от тук: 3. Bulls and Cows.doc

На пръв поглед нищо сложно. Завъртях 4 вложени цикъла и проверявах за всеки получен вариант дали сравнено с secret се получава търсения брой бикове и крави. Бързо се видя че в отговорите не участва цифрата 0. Проблема дойде от алгоритъма за проверка колко бика и крави има. При положение че има повторение на цифрите се получават няколко варианта за броене, една и съща цифра, която се среща няколко пъти, колко пъти ще се брои. Но в крайна сметка намерих желания вариант и се получи.
Важно е след като се преброи дадена цифра да не може повторно да се брои. И първо да се преброят биковете.

Ето и моето решение (като изтрих CheckBullsAndCows - това е първия вариант на функцията):
using System;
using System.Collections.Generic;

class BullsAndCows
{
    static bool CheckBullsAndCows2(int sd1, int sd2, int sd3, int sd4, int d1, int d2, int d3, int d4, int bulls, int cows)
    {
        int newBulls = 0;
        int newCows = 0;
        if (sd1 == d1)
        {
            newBulls++;
            sd1 = -2;
            d1 = -1;
        }
        if (sd2 == d2)
        {
            newBulls++;
            sd2 = -2;
            d2 = -1;
        }
        if (sd3 == d3)
        {
            newBulls++;
            sd3 = -2;
            d3 = -1;
        }
        if (sd4 == d4)
        {
            newBulls++;
            sd4 = -2;
            d4 = -1;
        }
        //sd1
        if (sd1 >= 0)
        {
            bool hasCow = false;
            if (sd1 == d2)
            {
                hasCow = true;
                d2 = -1;
            }
            if (sd1 == d3)
            {
                hasCow = true;
                d3 = -1;
            }
            if (sd1 == d4)
            {
                hasCow = true;
                d4 = -1;
            }
            if (hasCow)
            {
                newCows++;
            }
        }
        //sd2
        if (sd2 >= 0)
        {
            bool hasCow = false;
            if (sd2 == d1)
            {
                hasCow = true;
                d1 = -1;
            }
            if (sd2 == d3)
            {
                hasCow = true;
                d3 = -1;
            }
            if (sd2 == d4)
            {
                hasCow = true;
                d4 = -1;
            }
            if (hasCow)
            {
                newCows++;
            }
        }
        //sd3
        if (sd3 >= 0)
        {
            bool hasCow = false;
            if (sd3 == d1)
            {
                hasCow = true;
                d1 = -1;
            }
            if (sd3 == d2)
            {
                hasCow = true;
                d2 = -1;
            }
            if (sd3 == d4)
            {
                hasCow = true;
                d4 = -1;
            }
            if (hasCow)
            {
                newCows++;
            }
        }
        //sd4
        if (sd4 >= 0)
        {
            bool hasCow = false;
            if (sd4 == d1)
            {
                hasCow = true;
                d1 = -1;
            }
            if (sd4 == d2)
            {
                hasCow = true;
                d2 = -1;
            }
            if (sd4 == d3)
            {
                hasCow = true;
                d3 = -1;
            }
            if (hasCow)
            {
                newCows++;
            }
        }
        return ((bulls == newBulls) && (cows == newCows));
    }

    static void Main()
    {
        string secretNumber = Console.ReadLine();
        int[] secretDigits = new int[4];
        for (int i = 0; i < 4; i++)
        {
            secretDigits[i] = secretNumber[i] - '0';
        }
        int bulls = int.Parse(Console.ReadLine());
        int cows = int.Parse(Console.ReadLine());
        bool hasAtLeastOnePrinted = false;
        for (int digit1 = 1; digit1 < 10; digit1++)
        {
            for (int digit2 = 1; digit2 < 10; digit2++)
            {
                for (int digit3 = 1; digit3 < 10; digit3++)
                {
                    for (int digit4 = 1; digit4 < 10; digit4++)
                    {
                        if (CheckBullsAndCows2(secretDigits[0], secretDigits[1], secretDigits[2], secretDigits[3], digit1, digit2, digit3, digit4, bulls, cows))
                        {
                            if (hasAtLeastOnePrinted)
                            {
                                Console.Write(" {0}{1}{2}{3}", digit1, digit2, digit3, digit4);
                            }
                            else
                            {
                                Console.Write("{0}{1}{2}{3}", digit1, digit2, digit3, digit4);
                                hasAtLeastOnePrinted = true;
                            }
                        }
                    }
                }
            }
        }
        if (!hasAtLeastOnePrinted)
        {
            Console.Write("No");
        }
        Console.WriteLine();
    }
}


C# програмиране - част I - Problem 2 – Drunken Numbers - Изпит на 23.06.2013

Задача 1
Задача 2
Задача 3
Задача 4
Задача 5

Здравейте,
Ето и впечатленията ми от втората задача на изпита.

Условието на задачата може да изтеглите от тук: 2. Drunken Numbers.doc

Тази задача ми се стори лесна и започнах с нея, но не я направих от първия път :).
Искаше ми се да опиша целия път на всяка от задачите и трудностите които срещнах, но в момента нямам достъп в bgcoder до всички предадени варианти и техния код ще го карам малко наизуст.

Първо я направих с тип int за сумата на бирите за всеки от участниците. В последствие се оказа че не стига и сложих long, дори в крайна сметка го оставих с ulong понеже си е положително число.
В началото си запазвах входа в стринг. След това чистех нулите отпред както е описано и обхождах  стринга един път до средата и натрупвах сумите едновремено за двата играча. Накрая просто сравнявах и печатах правилното в случаите.

Но все на 70 точки стоях.

Пренаписах програмата да взимам числата с int.Parse - тук беше момента в който разбрах че int не стига. Това добре но типа не помогна, дори опитах с BigInteger и пак не помогна.

По едно време ми хрумна че може да има други символи, макар че BigInteger.Parse и ulong.Parse минаваха. Опитах да извлека от стринга само символите от 0 до 9. Тук една грешка ми изигра лоша шега и ми отне много още мислене и чудене защо още съм на 70 точки.
След доста лутане забелязах че първо чистя нулите отпред и после извличам от стринга само цифрите. След като им смених местата и най-накрая 10 мин. преди края на изпита - 100 точки.
Евала. Невероятно облекчение.

Ето и моето решение:

using System;
using System.Text;

class DrunkenNumbers
{
    static void Main()
    {
        int rounds = int.Parse(Console.ReadLine());
        ulong countMitko = 0;
        ulong countVladko = 0;
        for (int round = 0; round < rounds; round++)
        {
            string drunkenNumber = Console.ReadLine();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < drunkenNumber.Length; i++)
            {
                if (drunkenNumber[i] >= '0' && drunkenNumber[i] <= '9')
                {
                    sb.Append(drunkenNumber[i]);
                }
            }
            drunkenNumber = sb.ToString().TrimStart(new Char[] { '0' });
            int length = drunkenNumber.Length / 2 + drunkenNumber.Length % 2;
            for (int index = 0; index < length; index++)
            {
                if (drunkenNumber[index] >= '0' && drunkenNumber[index] <= '9')
                {
                    countMitko += (ulong)(drunkenNumber[index] - '0');
                }
                if (drunkenNumber[drunkenNumber.Length - 1 - index] >= '0' && drunkenNumber[drunkenNumber.Length - 1 - index] <= '9')
                {
                    countVladko += (ulong)(drunkenNumber[drunkenNumber.Length - 1 - index] - '0');
                }
            }
        }
        if (countMitko > countVladko)
        {
            Console.WriteLine("M {0}", (countMitko - countVladko));
        }
        else if (countMitko < countVladko)
        {
            Console.WriteLine("V {0}", (countVladko - countMitko));
        }
        else
        {
            Console.WriteLine("No {0}", (countMitko + countVladko));
        }
    }
}

C# програмиране - част I - Problem 1 – Coffee Vending Machine - Изпит на 23.06.2013

Задача 1
Задача 2
Задача 3
Задача 4
Задача 5

Здравейте,
Желая да споделя личното си впечатление от задачите на изпита. Ето и първата от тях.

Условието на задачата може да изтеглите от тук: 1. Coffee Machine.doc

В началото като прочетох условието и реших че тук ще се наложи да проверявам типа на монетите които имам за рестото и логично ако в машината има единствено монети от 1 лев, машината няма да може да върне ресто от 50 стотинки, поради липса на по-дребни монети.
Затова в началото реших че задачата не е толкова лесна, в последствие видях скорошни решения на задачата със 100 точки оценка и реших че едва ли се иска точно това, оказа се че трябва да се направят прости сметки дали има повече пари в машината от рестото.

Трите случая за отговор на програмата са:

1. Ако дадената сума от клиента е по-голяма или равна на цената, можем да сметнем рестото и ако рестото е по-малко или равно на сумата в машината, тогава може да се върне рестото (тук не е необходимо да се проверяват монетите и дали с тях може да се направи комбинация за да се получи рестото, което описах по-горе).
В този случай отпечатваме Yes, интервал и сумата останала в машината без рестото.

2. Ако дадената сума от клиента е по-малка от цената, тогава отпечатваме More, интервал и разликата от цената и дадената сума.

3. Ако дадената сума от клиента е по-голяма от цената и рестото което трябва да се върне е по-малко от сумата в машината отпечатваме No, интервал и разликата между рестото и сумата в машината.

Ето и моето решение:

using System;

class CoffeeVendingMachine
{
    static void Main()
    {
        int numOfCoins1 = int.Parse(Console.ReadLine());
        int numOfCoins2 = int.Parse(Console.ReadLine());
        int numOfCoins3 = int.Parse(Console.ReadLine());
        int numOfCoins4 = int.Parse(Console.ReadLine());
        int numOfCoins5 = int.Parse(Console.ReadLine());
        double machineTotal = 0.05 * numOfCoins1 + 0.1 * numOfCoins2 + 0.2 * numOfCoins3 + 0.5 * numOfCoins4 + 1.0 * numOfCoins5;
        double amount = double.Parse(Console.ReadLine());
        double price = double.Parse(Console.ReadLine());
        if (price > amount)
        {
            Console.WriteLine("More {0:0.00}", (price - amount));
        }
        else if (machineTotal < amount - price)
        {
            Console.WriteLine("No {0:0.00}", (amount - price - machineTotal));
        }
        else
        {
            Console.WriteLine("Yes {0:0.00}", (machineTotal - amount + price));
        }
    }
}