Raw File
two_opt_fast_solver.h
#pragma once

#include <bits/stdc++.h>

#include "Instance.h"

using namespace std;

namespace two_opt_fast_solver {
struct node {
	pt r0, r1; // robot (-1 means obstacle, 0 means empty, i means i-1th robot
	int t;		 // time of the graph node
	node(pt r0, pt r1, int time = 0) : r0(r0), r1(r1), t(time) {}
	const bool operator<(const node& o) const {
		if (t != o.t)
			return t > o.t;
		if (r0 != o.r0)
			return r0 < o.r0;
		return r1 < o.r1;
	}
};

struct two_opt_fast_solver {
	using state = pair<pt, int>;

	instance& ins;
	vector<vector<int>> moves; // first index is robot (transpose of move matrix)
	unordered_map<state, int> blocked_f;
	unordered_map<state, int> blocked_b;
	set<pt> blocked_s;
	bool spam_stdio;

	two_opt_fast_solver(instance& _ins) : ins(_ins), spam_stdio(true) {
		if (!verify(ins)) {
			cerr << "Requires valid initial sequence" << endl;
			assert(false);
		}

		moves.resize(ins.n);
		// convert dense moves into per-robot moves
		for (int t = 0; t < ins.time; t++) {
			for (int j = 0; j < ins.n; j++) {
				moves[j].push_back(ins.moves[t][j]);
			}
		}

		// for each robot, add moves as obstacles
		for (int i = 0; i < ins.n; i++) {
			add_path(i);
		}

		// block the obstacles
		for (pt p : ins.obstacle) {
			blocked_s.insert(p);
		}
	}

	two_opt_fast_solver(instance& _ins, bool _spam_stdio)
			: ins(_ins), spam_stdio(_spam_stdio) {
		if (!verify(ins)) {
			cerr << "Requires valid initial sequence" << endl;
			assert(false);
		}

		moves.resize(ins.n);
		// convert dense moves into per-robot moves
		for (int t = 0; t < ins.time; t++) {
			for (int j = 0; j < ins.n; j++) {
				moves[j].push_back(ins.moves[t][j]);
			}
		}

		// for each robot, add moves as obstacles
		for (int i = 0; i < ins.n; i++) {
			add_path(i);
		}

		// block the obstacles
		for (pt p : ins.obstacle) {
			blocked_s.insert(p);
		}
	}

	void add_path(int r) {
		pt p = ins.start[r];
		int L = max((int)moves[r].size(), ins.time);
		for (int t = 0; t <= L; t++) {
			if (t < (int)moves[r].size()) {
				blocked_f[state(p, t)] = moves[r][t];
				p = p + dxy[moves[r][t]];
				blocked_b[state(p, t)] = moves[r][t];
			} else {
				blocked_f[state(p, t)] = 0;
			}
		}
		assert(p == ins.target[r]);
	}

	// removes the path for a robot
	void remove_path(int r) {
		pt p = ins.start[r];
		int L = max((int)moves[r].size(), ins.time);
		for (int t = 0; t <= L; t++) {
			if (t < (int)moves[r].size()) {
				blocked_f.erase(state(p, t));
				p = p + dxy[moves[r][t]];
				blocked_b.erase(state(p, t));
			} else {
				blocked_f.erase(state(p, t));
			}
		}
		assert(p == ins.target[r]);
	}

	bool allowed(node s, int d0, int d1) {
		node ns(s.r0 + dxy[d0], s.r1 + dxy[d1], s.t + 1);
		// Obstacles
		if (blocked_s.count(ns.r0) || blocked_s.count(ns.r1))
			return false;

		// Some robot is already there
		if (blocked_f.count(state(ns.r0, ns.t)) ||
				blocked_f.count(state(ns.r1, ns.t)))
			return false;

		// Some robot is there right now and not moving away in the same dir
		if (blocked_f.count(state(ns.r0, s.t)) &&
				blocked_f[state(ns.r0, s.t)] != d0)
			return false;

		if (blocked_f.count(state(ns.r1, s.t)) &&
				blocked_f[state(ns.r1, s.t)] != d1)
			return false;

		// Some robot is moving into our spot and we're not moving away
		if (blocked_b.count(state(s.r0, s.t)) && blocked_b[state(s.r0, s.t)] != d0)
			return false;

		if (blocked_b.count(state(s.r1, s.t)) && blocked_b[state(s.r1, s.t)] != d1)
			return false;

		// The two robots interfere with each other
		if (abs(s.r0 - s.r1) == 1 && abs(ns.r0 - ns.r1) <= 1 && d0 != d1)
			return false;

		// The two robots collide
		if (ns.r0 == ns.r1)
			return false;

		return true;
	}

	void create_bounds(map<pt, bool>& res, pt p, const vector<int>& moveset, int R = 6) {
		for (int i = -1; i < (int) moveset.size(); i++) {
			if (i >= 0) p = p + dxy[moveset[i]];
			for (int x = p.x - R/2; x <= p.x + R/2; x++) {
				for (int y = p.y - R/2; y <= p.y + R/2; y++) {
					res[pt(x, y)] = true;
				}
			}
		}
	}

	// Solves the optimization problem for two robots
	void solve_two(int r0, int r1, double time_left) {
		pt t0 = ins.target[r0], t1 = ins.target[r1];

		map<pt, bool> in_bound;
		create_bounds(in_bound, ins.start[r0], moves[r0]);
		create_bounds(in_bound, ins.start[r1], moves[r1]);

		// remove the two robots' paths from moveset
		remove_path(r0);
		remove_path(r1);

		map<node, int> dist;
		map<node, pair<int, int>> par;

		auto comp = [&](const node& u, const node& v) {
			int s0 = dist[u] + abs(u.r0 - t0) + abs(u.r1 - t1);
			int s1 = dist[v] + abs(v.r0 - t0) + abs(v.r1 - t1);
			if (s0 != s1)
				return s0 < s1;
			return u < v;
		};

		node s(ins.start[r0], ins.start[r1], 0);
		set<node, decltype(comp)> q(comp);
		dist[s] = 0;
		q.insert(s);

		// Max time for search is remaining time
		auto start_time = clock();
		bool found = false;
		while (clock() - start_time < CLOCKS_PER_SEC * time_left) {
			s = *q.begin();
			q.erase(q.begin());
			if (s.r0 == t0 && s.r1 == t1 && s.t >= ins.time) {
				if (spam_stdio) {
					cerr << "Path found for " << ins.start[r0] << " " << ins.start[r1]
							 << "!" << endl;
					cerr << "Nodes expanded: " << dist.size() << endl;
				}
				found = true;
				break;
			}
			if (s.t >= ins.time)
				continue;

			for (int d0 = 0; d0 <= 4; d0++) {
				for (int d1 = 0; d1 <= 4; d1++) {
					if (allowed(s, d0, d1)) {
						node ns(s.r0 + dxy[d0], s.r1 + dxy[d1], s.t + 1);
						if (!in_bound.count(ns.r0) || !in_bound.count(ns.r1))
							continue;

						int ndist = dist[s] + (bool(d0) + bool(d1));
						if (!dist.count(ns) || ndist < dist[ns]) {
							q.erase(ns);
							dist[ns] = ndist;
							par[ns] = make_pair(d0, d1);
							q.insert(ns);
						}
					}
				}
			}
		}
		if (!found) {
			add_path(r0);
			add_path(r1);
			return;
		}

		moves[r0].clear();
		moves[r1].clear();
		while (s.t != 0) {
			auto move = par[s];
			moves[r0].push_back(move.first);
			moves[r1].push_back(move.second);
			s.r0 = s.r0 - dxy[move.first];
			s.r1 = s.r1 - dxy[move.second];
			s.t--;
		}
		reverse(moves[r0].begin(), moves[r0].end());
		reverse(moves[r1].begin(), moves[r1].end());
		// re-add robot paths
		add_path(r0);
		add_path(r1);
	}

	bool optimize(int seconds) {
		if (spam_stdio)
			cerr << "Given a compute budget of " << seconds << " seconds." << endl;
		auto ti = clock();

		// int sum;
		// map<int, pair<int, int>> dis;
		// auto resample = [&](){
		// 	dis.clear();
		// 	sum = 0;
		// 	for (int i = 0; i < ins.n; i++) {
		// 		for (int j = 0; j < ins.n; j++) {
		// 			int count = 0;
		// 			pt p = ins.start[i], q = ins.start[j];
		// 			for (int k = 0; k < (int) min(moves[i].size(), moves[j].size());
		// k++) { 				p = p + dxy[moves[i][k]]; 				q = q +
		// dxy[moves[j][k]]; 				if (abs(p-q) == 1
		// && moves[i][k] + moves[j][k] != 0) { 					count++;
		// 				}
		// 			}
		// 			sum += count;
		// 			dis[sum] = make_pair(i, j);
		// 		}
		// 	}
		// };
		// resample();
		// set<pair<int, int>> done;
		vector<pair<int, int>> pairs;
		for (int i = 0; i < ins.n; i++) {
			for (int j = i+1; j < ins.n; j++) {
				pairs.push_back({i, j});
			}
		}
		srand(time(0));
		random_shuffle(pairs.begin(), pairs.end());

		int r = 0;
		srand(time(NULL));
		while (r < (int) pairs.size() && clock() - ti < seconds * CLOCKS_PER_SEC) {
			int a = pairs[r].first, b = pairs[r].second;
			r++;

			// reoptimize wrt to two robots
			double curr_time = (clock() - ti) / double(CLOCKS_PER_SEC);
			solve_two(a, b, seconds - curr_time);
		}

		// Clean-up moves
		if (spam_stdio)
			cerr << "Old makespan: " << ins.time << endl;
		ins.time = 0;
		for (int i = 0; i < ins.n; i++) {
			ins.time = max(ins.time, (int)moves[i].size());
		}
		ins.moves.resize(ins.time);
		if (spam_stdio)
			cerr << "New makespan: " << ins.time << endl;

		// Transpose moves back into time-major
		for (int i = 0; i < ins.n; i++) {
			for (int t = 0; t < ins.time; t++) {
				if (ins.moves[t].empty())
					ins.moves[t].resize(ins.n);

				if (t >= (int)moves[i].size()) {
					ins.moves[t][i] = 0;
				} else {
					ins.moves[t][i] = moves[i][t];
				}
			}
		}

		bool cont;
		do {
			cont = false;
			if (accumulate(ins.moves.back().begin(), ins.moves.back().end(), 0) ==
					0) {
				ins.moves.pop_back();
				ins.time--;
				cont = true;
			}
		} while (cont);

		return true;
	}
};

// returns if improvement found
bool run(instance& ins, int seconds, bool spam_stdio = true) {
	two_opt_fast_solver gd(ins, spam_stdio);
	bool improve = gd.optimize(seconds);
	return improve;
}

} // namespace two_opt_fast_solver
back to top