2013-03-15 2 views
3

Как бы вы пишете что-то, что выбирает все возможные комбинации троек из массива {1, 2, 3, ..., N-1, N} без дубликатов? Это из недавнего конкурса программирования. N кратно 3.Поиск всех возможных комбинаций без дублирования выбора?

примера, используя массив {1,2,3,4,5,6}:

C_1 = { {1,2,3}, {4,5,6} } 
C_2 = { {1,2,4}, {3,5,6} } 
C_3 = { {1,2,5}, {3,4,6} } 

все действительны, но

C_bad1 = { {1,2,3}, {3, 4, 5} } 
C_bad2 = { {1,2,4}, {3, 5, 6}, {1, 2, 5} } 

нет.

+0

Являются ли {{1,2,3}, {4,5,6}} и {{4,5,6}, {1,2,3}} отдельными или дублирующими? IE, существуют ли 20 o 10 склеиваемых комбинаций троек для N = 6? –

+0

Эти наборы являются дубликатами. Проблема состоит в том, чтобы найти количество способов, с помощью которых команды из 3 могут быть сделаны из N учеников, и предоставить перечисление (например, C_i выше). И каждый из C_i будет иметь N/3 членов. – user1505713

+0

TY. Я приближался к нему с точки зрения перечисления, но прогресса пока нет. –

ответ

2

Поскольку N кратно 3, мы можем решить эту проблему, используя трюк:

  1. Генерировать все Перестановки чисел в порядке возрастания
  2. Для каждой перестановки, разбиение числа в наборах 3 непосредственно (0-2, 3-6, ..., N-2..N)

Это должно дать вам ваш результат без особых причудливых работ.

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

Шаг 3: Если какая-либо тройка лексикографически несортирована, отбросьте набор. Остальное продолжается.

+0

+1. и для генерации перестановок см. этот ответ: http://stackoverflow.com/questions/15122928/segmentation-fault-due-to-recursion/15123537#15123537 –

+3

Это не работает. 1,2,3,4,5,6; 4,5,6,1,2,3; 3,1,2,6,4,5 все одинаковы: набор {1,2,3} и набор {4,5,6} – Dave

1
#include <stdio.h> 

#define SEL_NUM 3 
#define LIST_SIZE 6 

void printset(int *list, int *map, int size); 
void select(int *list, int *map, int n, int size, int start); 

int main(int argc, const char **argv) { 
    int list[LIST_SIZE] = {1, 2, 3, 4, 5, 6}; 
    int map[LIST_SIZE] = {0}; 

    select(list, map, SEL_NUM, LIST_SIZE, 0); 

    return 0; 
} 

void select(int *list, int *map, int n, int size, int start) { 
    if (n == 0) { 
    printset(list, map, size); 
    return; 
    } 
    for (int i = start; i < size; i++) { 
    map[i] = 1; 
    select(list, map, n - 1, size, i + 1); 
    map[i] = 0; 
    } 
} 

void printset(int *list, int *map, int size) { 
    int list1[SEL_NUM], list2[SEL_NUM], list1cnt = 0, list2cnt = 0; 
    for (int i = 0; i < size; i++) 
    if (map[i]) 
     list1[list1cnt++] = list[i]; 
    else 
     list2[list2cnt++] = list[i]; 
    for (int i = 0; i < list1cnt; i++) 
    printf(" %d ", list1[i]); 
    printf(" -- "); 
    for (int i = 0; i < list2cnt; i++) 
    printf(" %d ", list2[i]); 
    printf("\n"); 
} 
+0

очень приятное решение :) – SomeWittyUsername

+0

спасибо, чувствует, что он может уточняется, хотя – perreal

+0

Этот алгоритм также генерирует дубликат, а не элемент # 1 для привязки к первому триплету каждого набора. –

2

у вас есть (N!)/(^ (N/3)) * ((N/3)!) ((3!)) Позиция (prove). вы можете просто использовать рекурсивный алгоритм для предоставления всех возможных комбинаций троек из массива {1, 2, 3, ..., N-1, N} без дубликатов. , но для производства одного из них вы можете использовать любую идею, такую ​​как user1952500 идея (хотя этот алгоритм также генерирует (N/3)! Дубликат позиции) или каждый, например, вы неизменяете last- (N-6) -member и ставите свой раствор для первого 6-члена в начале вашего результата (этот алгоритм не создает дубликат позиции)

рекурсивное решения:.

void combtriples(int begin) 
    { 
    for(int i=1;i<=(n/3);i++) 
     for(int j=1;j<=(n/3);j++) 
     for(int k=1;k<=(n/3);k++) 
     { 
     if ((mark[i]<3) && (mark[j]<3) && (mark[k]<3)) 
      { 
      count-position++; 
      c[count][3]=begin; 
      c[count][4]=begin+1; 
      c[count][5]=begin+2; 
      mark[i]++; 
      mark[j]++; 
      mark[k]++; 
      count-member-flase=count-member-flase+3; 
      if (count-member-flase > 0) 
      { 
      combtriples(begin+3); 
      } 
      } 
     } 
    } 


    int main() 
    { 
    int mark[]; 
    int c[][]; 
    count-position=0; 
    count-member-flase=0; 
    combtriples(1); 
    return 0; 
    } 
+0

Этот алгоритм генерирует дубликаты, что видно из того факта, что первый элемент не вынужден к первому триплету каждого набора. –

+0

Следующий тест: для N = 9 ваш код генерирует ровно 280 триплетных наборов? –

+0

c (9,3) * c (6,3) * c (3,3)/3! = 280 –

0

Давайте рассмотрим, как многие из них существуют такие различные триплетных наборы для N. первого определите T = floor (N/3) как количество триплетов в каждом наборе, поддерживаемых N элементами. Затем обратите внимание, что поскольку дублирующие триплеты нежелательны, триплеты в каждом наборе триплетов могут быть отсортированы по возрастанию по первому элементу без потери общности. Тогда общее количество триплетных множеств для N равно:

товар как t: 0 -> T-1 of ((N - 3 * t - 1) * (N - 3 * t - 2)/2)

Из этой формулы прямо видно, как построить алгоритм (грубой силы) для генерации триплетов.

Обновление: Вышеуказанное работает только для N% 3 == 0. Сейчас я работаю над обобщением. Forced; см комментарий по OP

случаи:

  1. N < 3 выходы 0
  2. N = 3 дает 1
  3. N = 6 дает (5 * 4/2) * (2 * 1/2) = 10
  4. N = 9 дает (8 * 7/2) * (5 * 4/2) * (2 * 1/2) = 28 * 10 = 280

Как вы в я предполагаю, что вам не нужен какой-либо код.

Обновление # 2: Обратите внимание, что для автоматического устранения дубликатов, первый элемент каждого триплета должен быть вынужден с наименьшим номером невыбранного элемента.

+0

Вы читаете мой ответ? –

+0

Пока нет. Я прочитал проблему перед сном и проснулся тем, что я тогда сюда вошёл. –

+0

@amink: вы создаете дубликаты; см. мою заметку № 2 выше. –

Смежные вопросы