Алгоритм перебора

206
14 января 2018, 05:13

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

Юного программиста Васю заинтересовал следующий вопрос: предположим, что мы взвешиваем боксёров на чашечных весах и пользуемся двумя двоичными наборами гирь: граммовым и килограммовым. Понятно, что граммовый набор является стандартным и состоит из гирек весом в 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 и 1024 грамм. Аналогично обстоит дело и с килограммовым набором – просто гири потяжелее, но при этом их немного меньше… Для простоты далее в этой задаче будем считать, что волшебным образом на прошедшей олимпиаде оказалось, что вес каждого спортсмена в точности мог быть измерен только килограммовым набором. Найти количество гирь.
40 <= вес спортсмена <= 130.

Суть в том, что я даже не понимаю что да как с этим алгоритмом. Что он должен перебирать? Рассмотрим пример моего решения.

У меня есть число n = 97 (для примера).
Его соседи из массива w[] есть 128 < 97 < 64.
Сперва проверим разницу между первым: 128 - 97 = 31, а потом и вторым: 97 - 64 = 33.
Видем, что 31 < 33, по-этому n = w[i] (тоесть 128) - n. А в другом случаи n -= w[i + 1] (тоесть 64, а не 128).
Делаем так до тех пор, пока n > 0.

Но, могу ли я считать это алгоритмом перебора? Я же здесь толком то и ничего не перебираю. Возможно Вы знаете какой-то другой вариант решения нужным мне способом!?

#include <iostream>
using namespace std;
int w[] = { 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 };
int neighbor(int n)
{
    int i = 0;
    while (n > 0)
    {
        if (n - w[i] < 0)
        {
            i++;
        }
        if (n - w[i] >= 0)
        {
            return i;
            break;
        }
    }
}
int main()
{
    int n;
    cin >> n;
    int i, count = 0;
    while (n > 0)
    {
        i = neighbor(n) - 1;
        if (w[i] - n < n - w[i + 1])
        {
            count++;
            n = w[i] - n;
        }
        else if (w[i] - n >= n - w[i + 1])
        {
            n -= w[i + 1];
            count++;
        }
    }
    cout << count << endl;
    system("pause");
    return 0;
}
Answer 1

Учитывая, что вес гирь это степени двойки, его можно рассматривать как набор бит в числе. Тогда нам будет проще работать в ними, как с обычными двоичными числами. Наша задача найти такой набор бит, который надо добавить к весу спортсмена и набор бит, которые положить на другую чашу. При этом нам надо что бы не было одинаковых бит среди добавленных на левую чашу и положенных на правую. Из всех вариантов нам надо выбрать тот, в котором будет минимальное количество бит в сумме из добавленных на левую и положенных на правую.

Просто пробуем перебрать все возможные наборы гирь, добавляемых на левую чашу. Если взять крайние случаи и вес спортсмена выходящий за рамки задачи нам надо будет проверить максимум 2047 вариантов.

int main() {
  int c,s,bits;
  int n=508;  // Вес спортсмена, находящегося на левой чаше весов
  int min=0xFFFF, min_c=0;
  for(c=0;c<=2047;c++) {
    s=n+c; // Добавляем набор гирь на левую чашу, получаем уравновешивающий
           // вес, необходимый на правой чаши
    if((s & c) != 0) // Если в добавленном наборе гирь на леву чашу
       continue;     // и необходимом наборе на правой чаше есть одинаковые
                     // гири - то не рассматриваем этот вариант
    s=s | c;         // Получаем сумму гирь на левой и правой чаше
    bits=0;
    for (; s; bits++) s &= (s - 1);  // И считаем их количество
    if(min > bits) {  // Если нашли количество меньшее, чем ранее, запоминаем
      min=bits;
      min_c=c;
    }
  }
  printf("min=%d for left %d right %d\n",min,min_c,min_c+n);
}
Answer 2

Мое решение имеет идею перебора , давайте представим , что каждому предмету мы можем сопоставить число 1 , если мы его берем на левые весы(то есть они прибавляют вес спортсмену) , число 0 , если они не используются и -1 если они противовес спортсмену.Тогда мы сможем перебрать все значение за 3^(k) , где k - количество предметов для задачи это 531441 операций , что нас устраивает.Код задачи приведен ниже.Засчитаное решение на e-olymp

#include <iostream>
using namespace std;
int x , p = 8;
int arr[12] = {0 , 1 , 2 , 4 , 8 , 16 , 32 , 64 , 128 , 256 , 512 , 1024} , k = 0;//индексация массива с елиницы
void du(int c , int r[12])
{
    if(c == 11)//когда мы набрали нужное количество гирь
    {  
        k++;
        int sum = 0 , l = 0;
        for(int i = 1;i < 12;i++)//проходим по предметам
        {
            sum += arr[i] * r[i];//в зависимости от места прибавляем , отнимаем или ничего не делаем
            if(r[i])
                l++;    
        }
        if(sum == x)
        {
            p = min(l , p);//нам нужно минимальное количество
        }
        return ;
    }
    //вызов всех возможных вариантов
    r[c+1] = 0;
    du(c+1 , r);
    r[c+1] = 1;
    du(c+1 , r);
    r[c+1] = -1;
    du(c+1  , r);
}
int main()
{
    cin >> x;
    int arr[12] = {0};//текущий набор(0 , 1 ,-1)согласно описанию више
    du(0 , arr);
    cout << p;//вывод ответа
}
READ ALSO
Создать ImageView, чтобы она отображалась пока работает программа

Создать ImageView, чтобы она отображалась пока работает программа

Есть приложение, надо создать ImageView, чтобы она отображалась пока работает программа

176
JSP IllegalStateException и JasperException

JSP IllegalStateException и JasperException

Пишу crud приложение по примерам из открытых источниковПри открытии моего единственного jsp файла

349
Google MVP в картах

Google MVP в картах

Я хочу использовать MVP и картуДля первого запуска работает все идеально, но когда я вращаю телефон получаю NullExeption в addMarkersOnMap потому что GoogleMap...

354
Как правильно подключить Java SDK от vk?

Как правильно подключить Java SDK от vk?

Извиняюсь за глупый вопрос но уже довольно долго сижу над элементарной вещьюДля работы с VK API решил использовать их SDK

343