Матч 302, Увеличение делителя (DivisorInc)

Дивизион 2, Уровень 3; Дивизион 1, Уровень 1

 

Имеется целое число k. К нему можно прибавить любой его делитель, отличный от 1 и k. К полученному числу снова применяется описанная операция. По заданным двум числам n и m необходимо найти минимальное количество таких операций, в результате которых можно получить из числа n число m.

 

Класс: DivisorInc

Метод: int countOperations(int N, int M)

Ограничения: 4 £ n £ 105, n £ m £ 105.

 

Вход. Два числа n и m.

 

Выход. Наименьшее количество описанных операций, необходимое для преобразования числа n в m. Если из n нельзя получить m, то вывести -1.

 

Пример входа

n

m

4

24

4

576

8748

83462

 

Пример выхода

5

14

10

 

 

РЕШЕНИЕ

поиск в ширину

 

Введем целочисленный массив used, в котором used[i] содержит наименьшее количество ходов, за которое из числа n можно получить i. Если used[i] = ¥, то получить i из n невозвожно (или значение еще не посчитано). Изначально положим used[n] = 0. Двигаемся по i от n до m – 1 и для каждого i, для которого used[i] ¹ ¥, находим его делители. Если j – делитель числа i (j £), то i/j также будет делителем i. Для каждого найденого делителя j числа i пересчитываем значение used[i + j]. Если used[i] + 1 < used[i + j], то положим used[i + j] = used[i] + 1. По окончании цикла по i результат находится в ячейке used[m], если оно не равно ¥. Ииначе возвращаем -1.

Отдельно следует обработать случай, когда n = m. Возвращаемое значение в таком случае равно 0.

 

ПРОГРАММА

 

#include <stdio.h>

#include <memory.h>

int used[200002];

 

class DivisorInc

{

public:

 

  int countOperations(int n, int m)

  {

    int i, j;

    if (n == m) return 0;

    memset(used,0x7F,sizeof(used));

    used[n] = 0;

    for(i = n; i < m; i++)

    {

      if (used[i] == 0x7F7F7F7F) continue;

      for(j = 2; j * j <= i; j++)

        if (i % j == 0)

        {

          if (used[i] + 1 < used[i + j]) used[i + j] = used[i] + 1;

          if (used[i] + 1 < used[i + (i/j)]) used[i + (i/j)] = used[i] + 1;

        }

    }

    return (used[m] == 0x7F7F7F7F) ? -1 : used[m];

  }

};

 

Реализация программы при помощи очереди.

 

#include <cstdio>

#include <deque>

using namespace std;

 

deque<pair<int,int> > d;

int i, flag = 0, used[100001];

 

class DivisorInc

{

public:

  int countOperations(int n, int m)

  {

    pair<int,int> p;

    if (n == m) return 0;

    memset(used,0,sizeof(used));

    d.push_back(make_pair(n,0));used[n] = 1;

    while(!d.empty())

    {

      p = d[0]; d.pop_front();

      if (p.first == m) {flag = 1; break;}

      for(i = 2; i * i <= p.first; i++)

        if (p.first % i == 0)

        {

          if((p.first+i <= m) && !used[p.first+i])

          {

            used[p.first + i] = 1;

            d.push_back(make_pair(p.first + i, p.second + 1));

          }

          if((p.first + p.first/i <= m) && !used[p.first + p.first/i])

          {

            used[p.first + p.first/i] = 1;

            d.push_back(make_pair(p.first + p.first/i, p.second + 1));

          }

        }

    }

    return flag ? p.second : -1;

  }

};