0

In how many ways can you put the numbers 1-16 into 16 sectors of circle such that the sum of any 3 consecutive sectors is less than 27 ? Any two sectors cannot have the same number.

[Source : AoPS]

I wanted to solve this problem using programming. The program I wrote is too slow (possibly 10^12 permutations being checked) despite my attempts at optimisation.

Logic behind my program (Filling sectors sequentially)

First I made sector[0] = 1 as a pivot. The remaining numbers (2-15) will be arranged in the remaining sectors with respect to this pivot. Then we fill sector[1]...sector[15] and stop when sector[0] is reached.

Given an empty sector,

If we don't know the values in the 2 previous sectors, try all possible numbers while keeping sum below 27.

Else find the sum of the 2 previous sectors, and for the current sector, try only the numbers that keep the sum below 27.

Move to next sector.

Stop when we reach sector with index 0. In that case check sum of sector 14, 15, 0 and 15, 0, 1.

#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;

unordered_map<int, int> UpdatedMap(unordered_map<int, int> Available, int key) {
    // Removes key from map and returns updated map
    Available.erase(key);
    return Available;
}

int count(vector<int> sector, int i, unordered_map<int, int> Available, int maxsum) {
    if (i == 0) {  // all 16 sectors are filled. validate.
        if (sector[sector.size() - 2] + sector[sector.size() - 1] + sector[0] <
            maxsum) {
            if (sector[sector.size() - 1] + sector[0] + sector[1] < maxsum) {
                return 1;
            } else {
                return 0;
            }
        }
    }

    int sum = 0;

    for (auto k : Available) {
        if (i == 1) {
            sector[i] = k.first;
            sum += count(sector, i + 1, UpdatedMap(Available, k.first), maxsum);
        } else {
            if (sector[i - 2] + sector[i - 1] + k.first <
                maxsum) {  // sum of last two sectors and current sector must be
                           // <27
                sector[i] = k.first;
                sum += count(sector, (i + 1) % sector.size(),
                             UpdatedMap(Available, k.first), maxsum);
            }
        }
    }
    return sum;
}

int main() {
    int k = 16;                         // k sectors and [1,k] numbers
    unordered_map<int, int> Available;  // only the key is being used. deletion
                                        // is easy with unordered map.
    vector<int> sector;
    for (int i = 1; i <= k; i++) {
        Available[i] = 1;
        sector.push_back(0);
    }
    sector[0] = 1;  // fixed pivot
    Available.erase(1);

    // fill sectors [1, k-1] with numbers [2, k]
    cout << count(sector, 1, Available, 27) << "\n";
    cout << "Done! \n";
}

With smaller values of k and arbitrarily large values of maxsum and the program outputs (k-1)! as expected. However when k=16, irrespective of the value of maxsum, the program becomes too slow.

Is there a better way to do this? I'm guessing maybe there's some kind of pattern which I missed.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
Bunny
  • 1,180
  • 8
  • 22
  • 1
    *"I wanted to solve this problem using programming"*. As bruteforce is too slow. It seems it is a mathematical question. (How many possible permutations, how many triplet are forbidden, ...). – Jarod42 Feb 16 '22 at 11:33
  • 1
    *"`unordered_map Available;"// only the key is being used`"*. So use `std::unordered_set`. – Jarod42 Feb 16 '22 at 11:37
  • With (ordered) `map`/`set`, `Available` would be sorted, and you might break as soon as a triplet is invalid (that should gain one level). – Jarod42 Feb 16 '22 at 11:48
  • `std::array` (or `vector`) might be better than the `std::set` with better caching (even if more test should be done to see if number is really available). – Jarod42 Feb 16 '22 at 11:50
  • Are you looking for permutations of 1..16, or just 16 values all within that range (and thus could contain duplicates)? If the former, why would "Any two sectors cannot have the same number." be needed? If the latter, then this question isn't about permutations (circular or otherwise), and you can't assume 1 will be used. – Scott Hunter Feb 16 '22 at 13:06
  • I added this "Any two sectors cannot have the same number." to specify that duplicates are not allowed. All numbers 1-16 must be used once only. Sorry if this was previously not clear. – Bunny Feb 16 '22 at 13:21
  • Is [`std::next_permutation`](https://en.cppreference.com/w/cpp/algorithm/next_permutation) out of the question? – Ted Lyngmo Feb 16 '22 at 23:03
  • How can we use `std::next_permutation` here? We also have to ensure that the sum of three consecutive sectors is less than 27. Generating a random permutation and then checking its sum will be slower right?. – Bunny Feb 17 '22 at 06:04

0 Answers0