//#define NDEBUG
#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <chrono>
#include <iostream>
#include <vector>
#include "boost/multi_array.hpp"
struct EndDistances;
using CityId = unsigned;
using DistanceType = unsigned;
using StoredDistanceType = unsigned char;
using CacheDistanceType = EndDistances;
using DistanceArray = boost::multi_array<DistanceType, 2>;
template<std::size_t size>
std::ostream& operator<<(std::ostream& out, const unsigned(& v)[size]) {
out << '{';
for(unsigned i=0; i<size; i++)
out << v[i]<<',';
return out << '}';
}
std::ostream& operator<<(std::ostream& out, const std::vector<unsigned>& v) {
out << '{';
for(unsigned i=0; i<v.size(); i++)
out << v[i]<<',';
return out << '}';
}
std::ostream& operator<<(std::ostream& out, const boost::detail::multi_array::sub_array<unsigned,1>& v) {
out << '{';
for(unsigned i=0; i<v.size(); i++)
out << v[i]<<',';
return out << '}';
}
struct EndDistances {
StoredDistanceType dists[7];
};
class TspCache {
std::vector<CacheDistanceType> cache;
public:
std::size_t size() {return cache.size();}
void assertEmpty(std::size_t idx) {assert(idx>=cache.size() || cache[idx].dists[0]==0);}
void put(std::size_t idx, CacheDistanceType distance) {if(cache.size()<=idx)cache.resize(idx+1); cache[idx] = distance;}
const CacheDistanceType& get(std::size_t idx) { assert(idx<cache.size()); return cache[idx];}
};
class TspSolver {
using OrderIdx = unsigned;
using OrderedCityArray = boost::multi_array<CityId, 2>;
unsigned city_count;
DistanceArray distances;
OrderedCityArray closest_cities;
TspCache end_cache;
DistanceType best_end_distance;
std::vector<CityId> current_cities;
unsigned current_count;
unsigned visited;
DistanceType current_distance;
std::vector<CityId> best_cities;
DistanceType best_distance;
void calculate_closest_cities() {
closest_cities.resize(boost::extents[city_count][city_count]);
for(CityId i=0; i<city_count; i++) {
for(CityId j=0; j<city_count; j++)
closest_cities[i][j] = j;
auto comp = [&](CityId l, CityId r){
if(l>0 && r>0) return distances[i][l] < distances[i][r];
return l>0;
};
std::sort(closest_cities[i].begin(), closest_cities[i].end(), comp);
}
}
template<unsigned k>
static unsigned n_choose_k(unsigned n) {
if (k > n) return 0;
if (k == n) return 1;
unsigned long long result = 1;
for( unsigned i = 1; i <= k; ++i ) {
result *= (n-i+1);
result /= i;
}
return result;
}
DistanceType find_end_distance(CityId* cities, DistanceType abortDistance) {
//assert(std::cout << "find_end_distance "<< cities[0] << ',' << cities[1] << ',' << cities[2] << ',' << cities[3] << ',' << cities[4] << ',' << cities[5] << " abortDistance="<<abortDistance<<std::flush);
assert(std::is_sorted(cities+1, cities+6));
DistanceType local_min = std::numeric_limits<DistanceType>::max();
std::array<CityId, 6> best;
do {
DistanceType dist = distances[cities[0]][cities[1]]
+ distances[cities[1]][cities[2]]
+ distances[cities[2]][cities[3]]
+ distances[cities[3]][cities[4]]
+ distances[cities[4]][cities[5]]
+ distances[cities[5]][0];
if (dist == abortDistance) {
//assert(std::cout << " found="<< cities[0] << ',' << cities[1] << ',' << cities[2] << ',' << cities[3] << ',' << cities[4] << ',' << cities[5]<<'\n'<<std::flush);
return abortDistance;
}
if (dist < local_min) {
local_min = dist;
std::copy(cities, cities+6, best.begin());
}
} while(std::next_permutation(cities+0, cities+6));
std::copy(best.begin(), best.end(), cities);
//assert(std::cout << " local_min="<<local_min<<'\n'<<std::flush);
return local_min;
}
void fill_cache() {
//assert(n_choose_k<7>(64) == 621216192);
best_end_distance = std::numeric_limits<DistanceType>::max();
unsigned count = 0;
for(CityId a=7; a<city_count; a++) {
unsigned ai = a-7;
unsigned am = n_choose_k<7>(a-1);
unsigned aa = 0+am;
for(CityId b=1; b<a; b++) {
unsigned bi = b-6;
unsigned bm = n_choose_k<6>(b-1);
unsigned ba = aa+bm;
for(CityId c=1; c<b; c++) {
unsigned ci = c-1;
unsigned cm = n_choose_k<5>(c-1);
unsigned ca = ba+cm;
for(CityId d=1; d<c; d++) {
unsigned di = d-1;
unsigned dm = n_choose_k<4>(d-1);
unsigned da = ca+dm;
for(CityId e=1; e<d; e++) {
unsigned ei = e-1;
unsigned em = n_choose_k<3>(e-1);
unsigned ea = da+em;
for(CityId f=1; f<e; f++) {
unsigned fi = f-1;
unsigned fm = n_choose_k<2>(f-1);
unsigned fa = ea+fm;
for(CityId g=1; g<f; g++) {
unsigned gi = g-1;
unsigned gm = gi; // n_choose_k<1>(g-1) is always g-1;
unsigned ga = fa+gm;
assert(std::cout << "fill_cache offsets=" <<ai<<'x'<<am << ',' <<bi<<'x'<<bm<< ',' <<ci<<'x'<<cm << ',' <<di<<'x'<<dm << ',' <<ei<<'x'<<em << ',' <<fi<<'x'<<fm << ',' <<gi<<'x'<<gm<<" ga="<<ga<<'\n' << std::flush);
std::array<CityId,6> cities = {{g, f, e, d, c, b}};
EndDistances ends;
ends.dists[0] = find_end_distance(cities.data(), 0);
ends.dists[0] += distances[a][cities[0]];
cities = {{g, f, e, d, c, a}};
ends.dists[1] = find_end_distance(cities.data(), 0);
ends.dists[1] += distances[b][cities[0]];
cities = {{g, f, e, d, b, a}};
ends.dists[2] = find_end_distance(cities.data(), 0);
ends.dists[2] += distances[c][cities[0]];
cities = {{g, f, e, c, b, a}};
ends.dists[3] = find_end_distance(cities.data(), 0);
ends.dists[3] += distances[d][cities[0]];
cities = {{g, f, d, c, b, a}};
ends.dists[4] = find_end_distance(cities.data(), 0);
ends.dists[4] += distances[d][cities[0]];
cities = {{g, e, d, c, b, a}};
ends.dists[5] = find_end_distance(cities.data(), 0);
ends.dists[5] += distances[f][cities[0]];
cities = {{f, e, d, c, b, a}};
ends.dists[6] = find_end_distance(cities.data(), 0);
ends.dists[6] += distances[g][cities[0]];
assert(std::cout << "fill_cache " << a << ',' << b << ',' << c << ',' << d << ',' << e << ',' << f << ',' << g << " idx=" << ga << '\n' << std::flush);
end_cache.assertEmpty(ga);
end_cache.put(ga, ends);
assert(end_cache.get(ga).dists[0]>0);
++count;
DistanceType local_min = *std::min(ends.dists+0, ends.dists+7);
if (local_min<best_end_distance)
best_end_distance = local_min;
}
}
}
}
}
}
}
std::cout << "Using " << (count*sizeof(CacheDistanceType)) << " out of " << (end_cache.size()*sizeof(CacheDistanceType)) << " allocated bytes\n";// << std::flush;
assert(std::cout << "fill_cache best_end_distance=" << best_end_distance << '\n' << std::flush);
}
void solve_end_fast() {
unsigned remain = visited;
CityId prev = current_cities[city_count-8];
assert(std::cout << "solve_end_fast count=" << current_count << " current=" << current_cities << " dist=" << current_distance << " prev_city=" << prev << " prev_visited="<<std::bitset<32>(visited)<<std::flush);
CityId* remainingIds = current_cities.data()+city_count-7;
CityId& a = remainingIds[0];
CityId& b = remainingIds[1];
CityId& c = remainingIds[2];
CityId& d = remainingIds[3];
CityId& e = remainingIds[4];
CityId& f = remainingIds[5];
CityId& g = remainingIds[6];
a = 31-__builtin_clz(remain);
remain &= ~(1u<<a);
b = 31-__builtin_clz(remain);
remain &= ~(1u<<b);
c = 31-__builtin_clz(remain);
remain &= ~(1u<<c);
d = 31-__builtin_clz(remain);
remain &= ~(1u<<d);
e = 31-__builtin_clz(remain);
remain &= ~(1u<<e);
f = 31-__builtin_clz(remain);
remain &= ~(1u<<f);
g = 31-__builtin_clz(remain);
assert(std::cout << " remaining="<< a << ',' << b << ',' << c << ',' << d << ',' << e << ',' << f << ',' << g << std::flush);
unsigned ai = a-7;
unsigned am = n_choose_k<7>(a-1);
unsigned aa = 0+am;
unsigned bi = b-6;
unsigned bm = n_choose_k<6>(b-1);
unsigned ba = aa+bm;
unsigned ci = c-1;
unsigned cm = n_choose_k<5>(c-1);
unsigned ca = ba+cm;
unsigned di = d-1;
unsigned dm = n_choose_k<4>(d-1);
unsigned da = ca+dm;
unsigned ei = e-1;
unsigned em = n_choose_k<3>(e-1);
unsigned ea = da+em;
unsigned fi = f-1;
unsigned fm = n_choose_k<2>(f-1);
unsigned fa = ea+fm;
unsigned gi = g-1;
unsigned gm = gi; // n_choose_k<1>(g-1) is always g-1;
unsigned ga = fa+gm;
assert(std::cout << " offsets=" <<ai<<'x'<<am << ',' <<bi<<'x'<<bm<< ',' <<ci<<'x'<<cm << ',' <<di<<'x'<<dm << ',' <<ei<<'x'<<em << ',' <<fi<<'x'<<fm << ',' <<gi<<'x'<<gm<<" ga="<<ga << std::flush);
const EndDistances& end_dist_array = end_cache.get(ga);
assert(end_dist_array.dists[0]>0);
EndDistances new_dist_array = {{
static_cast<StoredDistanceType>(current_distance + distances[prev][a] + end_dist_array.dists[0]),
static_cast<StoredDistanceType>(current_distance + distances[prev][b] + end_dist_array.dists[1]),
static_cast<StoredDistanceType>(current_distance + distances[prev][c] + end_dist_array.dists[2]),
static_cast<StoredDistanceType>(current_distance + distances[prev][d] + end_dist_array.dists[3]),
static_cast<StoredDistanceType>(current_distance + distances[prev][e] + end_dist_array.dists[4]),
static_cast<StoredDistanceType>(current_distance + distances[prev][f] + end_dist_array.dists[5]),
static_cast<StoredDistanceType>(current_distance + distances[prev][g] + end_dist_array.dists[6]),
}};
assert(std::cout << " dists="<<+end_dist_array.dists[0]<<','<<+end_dist_array.dists[1]<<','<<+end_dist_array.dists[2]<<','<<+end_dist_array.dists[3]<<','<<+end_dist_array.dists[4]<<','<<+end_dist_array.dists[5]<<','<<+end_dist_array.dists[6] << std::flush);
unsigned best_idx = std::min_element(new_dist_array.dists+0, new_dist_array.dists+7)-new_dist_array.dists+0;
DistanceType end_dist = end_dist_array.dists[best_idx];
DistanceType new_dist = new_dist_array.dists[best_idx];
assert(std::cout << " best_idx="<< best_idx << " first="<<remainingIds[best_idx]<<" end_dist=" << end_dist << " new_dist=" << new_dist << std::flush);
if (new_dist >= best_distance) {
assert(std::cout << " - exit because " << new_dist << ">=" << best_distance << '\n' << std::flush);
return;
}
assert(std::cout << '\n'<<std::flush);
assert(std::cout << "solve_end_slow beforeRotate=" << current_cities << '\n' << std::flush);
if (best_idx>0)
std::rotate(remainingIds+0, remainingIds+best_idx,remainingIds+best_idx+1);
std::reverse(remainingIds+1, remainingIds+7);
assert(std::cout << "solve_end_slow afterRotate=" << current_cities << '\n' << std::flush);
find_end_distance(¤t_cities[city_count-7], end_dist);
assert(std::cout << "solve_end_slow " << current_cities << " new best because " << new_dist << "<" << best_distance << '\n' << std::flush);
best_cities = current_cities;
best_distance = new_dist;
}
void solve_recursive() {
unsigned prev_count = current_count;
DistanceType prev_distance = current_distance;
unsigned prev_visited = visited;
CityId prev_city = current_cities[current_count-1];
assert(std::cout << "solve_recursive count=" << prev_count << " current=" << current_cities << " dist=" << prev_distance << " prev_city=" << prev_city << " prev_visited="<<std::bitset<32>(prev_visited)<<" closest=" << closest_cities[prev_city] << '\n' << std::flush);
++current_count;
for(OrderIdx i=0; i<city_count; i++) {
CityId c = closest_cities[prev_city][i];
if ((visited & (1u<<c)) == 0) continue;
current_cities[prev_count] = c;
current_distance = prev_distance + distances[prev_city][c];
if (current_distance + best_end_distance >= best_distance) {
assert(std::cout << "solve_recursive skip " << c << " because " << current_distance << " + " << best_end_distance << " >= " << best_distance << '\n' << std::flush);
continue;
}
visited = prev_visited & ~(1u<<c);
if (current_count == city_count-7)
solve_end_fast();
else
solve_recursive();
}
current_count = prev_count;
}
public:
TspSolver(DistanceArray distances_)
: distances(std::move(distances_))
{
city_count = distances.size();
calculate_closest_cities();
fill_cache();
}
std::vector<CityId> solve() {
current_cities.resize(city_count + 1, 0);
current_count = 1;
current_distance = 0;
visited = (1u<<city_count)-2u;
best_cities.resize(city_count + 1, 0);
best_distance = std::numeric_limits<DistanceType>::max();
solve_recursive();
return best_cities;
}
};
DistanceArray get_distances() {
std::istream& in = std::cin;
unsigned city_count;
in >> city_count;
DistanceArray distances(boost::extents[city_count][city_count]);
for(CityId i=0; i<city_count-1; i++) {
distances[i][i] = std::numeric_limits<DistanceType>::max();
for(CityId j=i+1; j<city_count; j++) {
in >> distances[i][j];
distances[j][i] = distances[i][j];
}
}
assert(in);
return distances;
}
int main() {
auto start = std::chrono::high_resolution_clock::now();
TspSolver solver(get_distances());
std::vector<unsigned> cities = solver.solve();
auto end = std::chrono::high_resolution_clock::now();
for(CityId i=0; i<cities.size(); i++)
std::cout << cities[i] << ',';
std::cout << ' ' << duration_cast<std::chrono::milliseconds>(end-start).count() << "ms\n";
}
10 6 7 2 2 7 3 4 8 2 2 6 4 5 5 5 3 6 6 5 6 5 4 2 6 2 8 4 3 6 2 6 3 3 7 3 3 6 8 5 6 9 3 6 6 11