Модераторы: Alx, Fixin
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> [c++] задачи с олимпиады, олимпиадные задачи по информатике 
:(
    Опции темы
PIvO
Дата 9.6.2007, 18:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 13.4.2007

Репутация: нет
Всего: нет



На олимпиаде по информатике было 4 задачки:
1. Даны натуральные числа m и n. Найти такие числа m1 и n1, не имеющие общих делителей, что m1/n1=m/n. Числа m и n ввести с клавиатуры.
2. Дано натуральное число n. Напечатать в порядке возрастания все простые несократимые дроби, заключенные между 0 и 1, знаменатели которых не привышают n. Дроби выводить в формате p/q. Число n задать с клавиатуры.
3. Имеется прямоугольный лист бумаги, длина которого равна N см, а ширина M см. С листом можно производить следующие операции: сгибать лист вдвое, совмещая противоположные стороны; сгибать лист, совмещая одну сторону с параллельной ей линией сгиба; разгибать лист при этом оставляя на нем линию сгиба. Написать программу, которая определяет: можно ли его свернуть так, чтобы получился прямоугольник длиной P см и шириной Q см. В случае утвердительного ответа программа должна выдавать минимальное количество операций с листом, необходимых для этого.
N, M, P и Q - дробно-рациональные числа, каждое из которых задается своим числителем и знаменателем. Числа вводятся с клавиатуры в виде "p,q", где p - числитель, а q - знаменатель.
Если лист свернуть можно, то ответ должен содержать "ДА". В противном случае - "НЕТ".
4. Имеется некий лабиринт неизвестной структуры. По лабиринту движется робот. На каждом шагесвоего движения робот делает шаг вперед или разворачивается влево (вправо) на 90 градусов. Весь путь движения робота описывается символьной строкой длиной не более 80 символов. Символ F означает движение на шаг вперед, L, R - поворот на 90 градусов влево или вправо соответственно.
Есть предположение, что в процессе своего движения по лабиринту робот может ходить кругами, т. е. пересекать ранее пройденные точки, или поворачиваться в неправильную сторону (3 раза налево вместо 1 направо). Задача заключается в том, чтобы сократить маршрут движения робота, убрав из него все петли и лишние повороты. Входная строка, описывающая исходный маршрут движения, вводится пользов телем с экрана. На выход необходимо выдать строку, описывающую сокращенный маршрут движения.
Прошу написать варианты решений (кто как думает).
PM MAIL   Вверх
Melarosa
Дата 12.6.2007, 17:40 (ссылка)    | (голосов:3) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 1
Регистрация: 12.6.2007

Репутация: нет
Всего: нет



Это программка ко второй задаче
Код

#include<iostream.h>
#include<conio.h>

void main()
{
    int i=1,j,n;
    clrscr();
    cout<<"vvedite chislo"<<endl;
    cin>>n;
    while(i<n)
    {
        for(j=1;j<n;j++)
        {
            if(i==1)
            cout<<i<<'/'<<j<<'\t';
            if(j%i&&i<j)
            cout<<i<<'/'<<j<<'\t';

        }
        i++;
    }
    getch();
}

Мож кто придумает проще?)


M
Pakshin A. S.
Не забываем выделять код специальными тегами!



Это сообщение отредактировал(а) Pakshin A. S. - 18.6.2007, 18:38
PM MAIL   Вверх
powerfox
Дата 18.6.2007, 17:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


I wanna fork()
****


Профиль
Группа: Комодератор
Сообщений: 3990
Регистрация: 1.10.2005
Где: Санкт-Петербург

Репутация: нет
Всего: 97



Для четвёртой, наверное, ввел бы систему координат и запоминал пройдённые точки, затем каждый раз проверял, была ли такая точка. Через дерево, наверное, тоже можно. Составить его маршрут в виде дерева: корень - конец марщрута, при составлении "взвешивать его", по завершению составления можно найти наикротчайший маршрут и получить нужную строку.

Добавлено @ 17:29
Цитата(PIvO @  9.6.2007,  19:09 Найти цитируемый пост)
1. Даны натуральные числа m и n. Найти такие числа m1 и n1, не имеющие общих делителей, что m1/n1=m/n. Числа m и n ввести с клавиатуры.

m1n=n1m, подбираем такие натуральные m1 и n1 и проверяем наличие общих делителей. По идее, не сложно, если не изощряться.

Добавлено @ 17:37
Цитата(PIvO @  9.6.2007,  19:09 Найти цитируемый пост)
1. Даны натуральные числа m и n. Найти такие числа m1 и n1, не имеющие общих делителей, что m1/n1=m/n. Числа m и n ввести с клавиатуры.

Сейчас думать некогда, но пришла такая идея. Подбором - слишком долго и просто. Есть 2 неизвестных, стало быть нужно получить >=2-х уравнений.

m div n = p div q
m mod n = p mod q

p = (m div n) * q
m mod n = ( (m div n) * q) mod q

Отсюда, по идее, можно выразить q.
А потом найти p.

Нужно проверить, не уверен, что пашет. Вечером или завтра код набью.

Это сообщение отредактировал(а) powerfox - 18.6.2007, 17:52


--------------------
user posted image
PM WWW   Вверх
Pakshin A. S.
Дата 18.6.2007, 18:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник Клуба
Сообщений: 5056
Регистрация: 16.2.2003

Репутация: нет
Всего: 61




 ! 
Pakshin A. S.
PIvO, не стоит создавать дубликаты темы; для решение проблем достаточно создать одну тему...

PM   Вверх
powerfox
Дата 18.6.2007, 19:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


I wanna fork()
****


Профиль
Группа: Комодератор
Сообщений: 3990
Регистрация: 1.10.2005
Где: Санкт-Петербург

Репутация: нет
Всего: 97



Цитата(powerfox @  18.6.2007,  18:27 Найти цитируемый пост)
Через дерево, наверное, тоже можно. Составить его маршрут в виде дерева: корень - конец марщрута, при составлении "взвешивать его", по завершению составления можно найти наикротчайший маршрут и получить нужную строку.

Как мне сказал Void, такое дерево зовётся графом. Точнее, это граф, а не дерево.


--------------------
user posted image
PM WWW   Вверх
SelenIT
Дата 21.6.2007, 21:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


баг форума
****


Профиль
Группа: Завсегдатай
Сообщений: 3996
Регистрация: 17.10.2006
Где: Pale Blue Dot

Репутация: 4
Всего: 401



Имхо, первая задача - это, фактически, обычное сокращение дроби: нужно найти наибольший общий делитель m и n (например, алгоритмом Евклида) и разделить оба числа на него...


--------------------
Осторожно! Данный юзер и его посты содержат ДГМО! Противопоказано лицам с предрасположенностью к зонеризму!
PM MAIL   Вверх
powerfox
Дата 21.6.2007, 22:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


I wanna fork()
****


Профиль
Группа: Комодератор
Сообщений: 3990
Регистрация: 1.10.2005
Где: Санкт-Петербург

Репутация: нет
Всего: 97



Цитата(SelenIT @  21.6.2007,  22:46 Найти цитируемый пост)
Имхо, первая задача - это, фактически, обычное сокращение дроби: нужно найти наибольший общий делитель m и n (например, алгоритмом Евклида) и разделить оба числа на него... 

Так не пойдёт, так как у чисел может и не быть общих делителей, а кратное обязательно есть.


--------------------
user posted image
PM WWW   Вверх
SelenIT
Дата 21.6.2007, 22:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


баг форума
****


Профиль
Группа: Завсегдатай
Сообщений: 3996
Регистрация: 17.10.2006
Где: Pale Blue Dot

Репутация: 4
Всего: 401



powerfox, сорри, можно чуть подробнее? Каким образом тогда сохранится пропорция?


--------------------
Осторожно! Данный юзер и его посты содержат ДГМО! Противопоказано лицам с предрасположенностью к зонеризму!
PM MAIL   Вверх
powerfox
Дата 21.6.2007, 22:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


I wanna fork()
****


Профиль
Группа: Комодератор
Сообщений: 3990
Регистрация: 1.10.2005
Где: Санкт-Петербург

Репутация: нет
Всего: 97



SelenIT, 
допустип числа 11 и 7.
У них нет общего делителя.
11     22
---  =  ---             здесь нарушается условие, что числа не должны иметь делителя, а очевидно, что делитель 2. Но можно подобрать такие, что будет выполняться
7       14

Например, 33/21. 

Твоё решение будет работать, только если дробь будет сократима (как раз найти наибольший делитель и поделить на него).


--------------------
user posted image
PM WWW   Вверх
SelenIT
Дата 21.6.2007, 23:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


баг форума
****


Профиль
Группа: Завсегдатай
Сообщений: 3996
Регистрация: 17.10.2006
Где: Pale Blue Dot

Репутация: 4
Всего: 401



powerfox, имхо, если дробь несократима, единственное решение задачи - сами m и n (по-моему, условие этого не запрещает). Любые другие пары чисел, удовлетворяющих пропорции, непременно будут иметь общий делитель. Разве не так?


--------------------
Осторожно! Данный юзер и его посты содержат ДГМО! Противопоказано лицам с предрасположенностью к зонеризму!
PM MAIL   Вверх
powerfox
Дата 22.6.2007, 00:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


I wanna fork()
****


Профиль
Группа: Комодератор
Сообщений: 3990
Регистрация: 1.10.2005
Где: Санкт-Петербург

Репутация: нет
Всего: 97



Цитата(SelenIT @  22.6.2007,  00:15 Найти цитируемый пост)
owerfox, имхо, если дробь несократима, единственное решение задачи - сами m и n (по-моему, условие этого не запрещает). Любые другие пары чисел, удовлетворяющих пропорции, непременно будут иметь общий делитель. Разве не так? 

Ты прав. Я не подумал, что 33 и 21 делятся на 3 smile
Действительно, просто алгоритм Евклида.


--------------------
user posted image
PM WWW   Вверх
SelenIT
Дата 22.6.2007, 04:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


баг форума
****


Профиль
Группа: Завсегдатай
Сообщений: 3996
Регистрация: 17.10.2006
Где: Pale Blue Dot

Репутация: 4
Всего: 401



Вот какой ужас получился у меня для второй задачи (привожу решение на... Javascript, чтобы можно было протестить алгоритм прямо в браузере, не тратя время на компиляцию):
Код

<script>

function nod(i,j) { // по сути, решение первой задачи
    if (i>j) return nod(j,i)
    var n;
    while(i>0) {
       n=i; i=(j%i); j=n;
    }
    return n;
}

function main(n) { // основная ф-ция
    var s=[], d; // вспомогательные переменные - буфер вывода и НОД
    var p=1, q=parseInt(n); // инициализация
    while (p*n <= (n - 1)*q) // реальный интервал, очевидно, от 1/n до (n-1)/n
    {
        s.push(p + '/' + q); // вывод в "буфер" (у меня это массив - так удобнее)
        for (var i=n; i>=1; i--) // ищем ближайшую след. дробь, сократимую до подходящего знаменателя
        {
            d = nod(p*i + 1, q*i);
            if (q*i <= n*d)
            {
                p = (p*i + 1) / d;
                q = q*i / d;
                break;
            }
        }
    }
    document.getElementById('oo').innerHTML = 
        '<div style="float: left; width: 6em;">' + 
        s.join('</div><div style="float: left; width: 6em;">') + 
        '</div>'; // вывод "буфера"
}

</script>
<i>n</i>: <input id="in">
<button type="button" onclick="main(document.getElementById('in').value)">Рассчитать</button>
<div id="oo"></div>

Полагаю, перевести логику на C++ не проблема, тем более синтаксис похож - у меня в MS VC++ 2005 заработало, хотя я вообще C++ не знаю... ;)

Зато, в отличие от варианта Melarosы, оно выводит дроби по возрастанию (в соответствии с ТЗ) и не оставляет вещей типа 4/6...

P.S. Небольшое пояснение: т.к. знаменатель по условию не может превосходить n, то разность соседних дробей p1/q1 - p0/q0 ≥ 1/(n*q0) ≥ 1/(n*(n-1)) > 1/n² (например, при n=9, первые дроби - 1/9 и 1/8 - различаются на 1/72). Алгоритм ищет минимальную разность (т.е. максимальный общий знаменатель), при котором следующая дробь сократима до подходящего (не превышающего n) знаменателя, перебирая потенциально допустимые общие знаменатели по убыванию. Чувствую, что его можно еще изрядно оптимизировать...

Это сообщение отредактировал(а) SelenIT - 2.7.2007, 02:11


--------------------
Осторожно! Данный юзер и его посты содержат ДГМО! Противопоказано лицам с предрасположенностью к зонеризму!
PM MAIL   Вверх
SelenIT
Дата 24.6.2007, 15:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


баг форума
****


Профиль
Группа: Завсегдатай
Сообщений: 3996
Регистрация: 17.10.2006
Где: Pale Blue Dot

Репутация: 4
Всего: 401



К третьей задаче: насколько я понимаю, для получения ответа ДА должны выполняться условия:
Цитата
   P/M = x/(2 в степени n) ≤ 1, Q/N = y/(2 в степени m) ≤ 1

или же
Цитата
   Q/M = x/(2 в степени n) ≤ 1, P/N = y/(2 в степени m) ≤ 1

где x, y, m и n - натуральные числа. Соответственно, у решения будут 2 ветви, каждая из которых опять же сводится к сокращению 2-х дробей и проверке знаменателя на принадлежность к ряду степеней двойки (по идее, можно битовыми операциями).

Что же до минимально нужного числа шагов, по-видимому, оценка нижней границы равна m+n. Насколько реальное количество шагов больше (сколько раз придется разгибать) - видимо, нужно анализировать x и y... есть интуитивная пока непроверенная догадка, что нужно считать нули в их двоичной записи...

Это сообщение отредактировал(а) SelenIT - 24.6.2007, 19:17


--------------------
Осторожно! Данный юзер и его посты содержат ДГМО! Противопоказано лицам с предрасположенностью к зонеризму!
PM MAIL   Вверх
maxdiver
Дата 20.12.2008, 09:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 381
Регистрация: 29.1.2008
Где: Саратов

Репутация: 2
Всего: 18



Цитата
2. Дано натуральное число n. Напечатать в порядке возрастания все простые несократимые дроби, заключенные между 0 и 1, знаменатели которых не привышают n. Дроби выводить в формате p/q. Число n задать с клавиатуры.

Эту задачу можно решить безо всяких gcd и оптимизаций.
Гуглим по "ряд Фарея" - и будет вам алгоритм за линейное (относительно количества дробей в ответе) время.

Вообще, давать такие задачи на олимпиаде по программированию - плохой стиль. Кто-то, кто знает ряд Фарея или подобную систему Штерна-Броко, решит её за 5 минут, а другой может думать 2 часа и не придумать - это совсем не тривиальный алгоритм. (Если, конечно, там не маленькие ограничения на N были даны)

Это сообщение отредактировал(а) maxdiver - 20.12.2008, 09:45
PM MAIL WWW ICQ   Вверх
Sartorius
Дата 20.12.2008, 15:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1568
Регистрация: 18.7.2006
Где: Ivory tower

Репутация: нет
Всего: 37



Код

#include <vector>
#include <string>
#include <iostream>
#include <algorithm>

using namespace std;

struct SCoord {
    SCoord(int _x, int _y)
    {
        x = _x; y = _y;
    }    
    bool operator== (const SCoord &rhs)
    {
        return (x == rhs.x && y == rhs.y);
    }
    int x, y;    
};

vector<SCoord> makeTraceFromString(string str)
{
    vector<SCoord> vTrace;
    vector<SCoord>::iterator iter;
    SCoord current(0, 0);
    int direction = 0; /* Direction */
    int dx[] = {0, 1, 0, -1};
    int dy[] = {1, 0, -1, 0};

    vTrace.push_back( SCoord(0, 0) );
    
    for (int i = 0; i < str.length(); i++)
    {
        switch (str[i])
        {
        case 'F':
            current.x += dx[direction];
            current.y += dy[direction];
            if ((iter = find(vTrace.begin(), vTrace.end(), current)) != vTrace.end()) //Already visited
            {
                vTrace.erase(iter + 1, vTrace.end());
            }
            else
            {
                vTrace.push_back(current);
            }
            break;
        case 'L':
            direction = (direction + 4 - 1) % 4;
            break;
        case 'R':
            direction = (direction + 1) % 4;
            break;
        }
    }
    return (vTrace);
}

string makeStringFromTrace(vector<SCoord> vTrace)
{
    string str;
    SCoord current(0, 0);

    int direction = 0; /* Direction */
    int dx[] = {0, 1, 0, -1};
    int dy[] = {1, 0, -1, 0};
    int ddirection[4][4] = 
    {
        {0, 1, 2, -1}, /* old direction = 0 */
        {-1, 0, 1, 2}, /* old direction = 1 */
        {2, -1, 0, 1}, /* old direction = 2 */
        {1, -2, -1, 0}  /* old direction = 3 */
    };

    for (int i = 1; i < vTrace.size(); i++)
    {
        /* find direction */
        for (int j = 0; j < 4; j++)
        {
            if ((dx[j] == vTrace[i].x - current.x) && (dy[j] == vTrace[i].y - current.y))
                break;
        }

        for (int k = 0; k < abs(ddirection[direction][j]); k++)
        {
            if (ddirection[direction][j] > 0) 
            {
                str += "R";
            }
            else
            {
                str += "L";
            }
        }

        str += "F";

        direction = j;
        current = vTrace[i];
    }

    return (str);
}



void main()
{
  cout << makeStringFromTrace( makeTraceFromString("FFFFRLRFFFRFFFRFFFF") );
}


для последней задачки
PM MAIL ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Интересные и занимательные задачи по программированию | Следующая тема »


 




[ Время генерации скрипта: 0.0576 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.