Raw File
sparse_graph.h
#pragma once

#include <algorithm>
#include <bits/stdc++.h>
#include <cassert>
#include <iostream>
#include <queue>
#include <set>
#include <tuple>
#include <unordered_map>

#include "Instance.h"
#include "sorted_set.h"

using namespace std;

/* NOTE THIS IS DUPLICATE OF graph.h BUT USES SPARSE REPRESENTATION OF MOVES
 * Usage:
 *  -   initialize with `graph g(ins, [max time horizon]);` default max time
 * horizon is ins.maxt
 *  -   if you make changes to the graph that you want to push to the instace,
 *        call `g.update_instance();`
 *  -   if you make changes to underlying instance you want to update graph
 * with, call `g.build([max time horizon])`
 *  -   g.robots() stores the last known location of the robot
 */

namespace sparse_graph {
struct node {
	int r; // robot (-1 means obstacle, 0 means empty, i means i-1th robot
	int d; // direction that robot went in
	int t; // time of the graph node
	node(int robot = 0, int dir = 0, int time = 0) : r(robot), d(dir), t(time) {}
	void reset() { r = d = t = 0; }
	bool operator==(const node& o) const {
		return tie(r, d, t) == tie(o.r, o.d, o.t);
	}
} null(0, 0, 0);

inline bool operator<(const node& a, const node& b) { return a.t < b.t; }

struct sparse_graph {

	// Assume coordinates range from [-OFF, DIM-OFF]
	static const int DIM = 440;
	static const int OFF = 170;
	static const short SINF = 0x3f3f;
	static const int MAXT = 2000;
	int maxt;
	instance& ins;
	// g[p] stores a list of actions, either a move, or a stay still.
	// If last action was to stay still, robot is still there.
	vector<vector<sorted_set<node>>> g;
	vector<vector<char>> obst;
	vector<int> robots; // latest node we have for a robot
	mt19937 rng;

	// initialize graph
	void build(int t = -1, const set<int>& ignore = set<int>()) {
		rng = mt19937(time(0));
		maxt = max(t + 1, (int)ins.moves.size() + 1);
		g.clear();
		mem_dist.clear();
		mem_dist.resize(ins.n);
		g.resize(DIM, vector<sorted_set<node>>(DIM));
		robots.resize(ins.n);
		obst.resize(DIM, vector<char>(DIM));
		// fill obstacles
		for (pt& p : ins.obstacle) {
			// g[p.x + OFF][p.y + OFF].insert(node(-1));
			obst[p.x + OFF][p.y + OFF] = 1;
		}
		// add obstacles around borders
		for (int i = 0; i < DIM; i++) {
			/*
			g[i][0].insert(node(-1));
			g[0][i].insert(node(-1));
			g[DIM-1][i].insert(node(-1));
			g[i][DIM-1].insert(node(-1));*/
			obst[i][0] = 1;
			obst[0][i] = 1;
			obst[DIM - 1][i] = 1;
			obst[i][DIM - 1] = 1;
		}

		// fill paths
		vector<pt> locations = ins.start;
		int curt = 0;
		// add a no-move at the end to indicate robot is still here.
		ins.moves.push_back(vector<int>(ins.n, 0));
		for (auto& step : ins.moves) {
			for (int i = 0; i < ins.n; ++i) {
				// don't add things from the ignore set to the graph at all
				if (ignore.count(i) > 0)
					continue;

				pt p = locations[i];
				robots[i] = add_move(hsh(p, curt), i, step[i]);
				p = p + dxy[step[i]];
				locations[i] = p;
				if (!in_range(p)) {
					cerr << "ERROR : Out of range coordinate considered " << p.x << " "
							 << p.y << endl;
					cerr << "Consider changing limits in the code." << endl;
					assert(false);
				}
			}
			curt++;
		}
		ins.moves.pop_back(); // remove the no move at end we added.
		for (int r = 0; r < ins.n; r++) {
			compute_dist(r);
		}
	}

	sparse_graph(instance& _ins, int _maxt = -1) : ins(_ins) { build(_maxt); }
	sparse_graph(instance& _ins, const vector<size_t>& ignore_v, int _maxt = -1)
			: ins(_ins) {
		set<int> ignore(ignore_v.begin(), ignore_v.end());
		build(_maxt, ignore);
	}

	// hash state to single int for convenience
	inline int hsh(int x, int y, int t = 0) {
		return (x + OFF) + ((y + OFF) << 9) + (t << 18);
	}
	inline int hsh(const pt p, int t = 0) { return hsh(p.x, p.y, t); }

	// unhash state
	inline int get_x(int h) { return (h & ((1 << 9) - 1)) - OFF; }
	inline int get_y(int h) { return ((h & ((1 << 18) - 1)) >> 9) - OFF; }
	inline pt get_p(int h) { return pt(get_x(h), get_y(h)); }
	inline int get_t(int h) { return h >> 18; }

	inline bool is_obstacle(const pt& p) { return obst[p.x + OFF][p.y + OFF]; }
	inline bool in_range(const pt& p) { return !is_obstacle(p); }

	// get last notable move relative to time h
	// Requires that the point is in_range
	inline node get_last_move(int h) {
		pt p = get_p(h);
		if (g[p.x + OFF][p.y + OFF].empty())
			return null;
		int t = get_t(h);
		// search through action list at p
		auto pt = g[p.x + OFF][p.y + OFF].lower_bound(node(0, 0, t));
		if (pt != g[p.x + OFF][p.y + OFF].end() && pt->t == t)
			return *pt;
		if (pt == g[p.x + OFF][p.y + OFF].begin())
			return null; // no robot was here ever
		pt = prev(pt);
		if (pt->d == 0)
			return node(pt->r, 0, t); // the last robot that was here hasn't moved yet
		return null;
	}

	inline bool is_last_move(int h) {
		pt p = get_p(h);
		int t = get_t(h);
		return g[p.x + OFF][p.y + OFF].lower_bound(node(0, 0, t)) ==
					 g[p.x + OFF][p.y + OFF].end();
	}

	// adds move at pos-time h, robot r, direction d, assumes move is valid
	// return last state recorded
	inline int add_move(int h, int r, int d) {
		int t = get_t(h);
		pt p = get_p(h);
		if (d == 0) {
			node last = get_last_move(h);
			if (last.r == r + 1 && last.d == 0) {
				return hsh(p, last.t); // last move was same robot still
			}
		}
		maxt = max(maxt, t + 1);
		g[p.x + OFF][p.y + OFF].insert(node(r + 1, d, t));
		return hsh(p, t);
	}

	// bad pun
	// assumes move was in the graph
	inline void re_move(int h, int r, int d) {
		int t = get_t(h);
		pt p = get_p(h);
		node last = get_last_move(h);
		if (d == 0) {
			if (last.r == r && last.d == 0 && last.t < t) {
				return; // last move was same robot still
			}
		}
		// technically maxt may decrease, but we can't really tell from here
		g[p.x + OFF][p.y + OFF].erase(last);
	}

	// remove path taken of robot r, returns length of path for reasons
	int remove_path(int r, int time = 0) {
		pt p = ins.start[r];
		int lastt = get_t(robots[r]);
		vector<int> mv;
		int moves = 0;
		for (int t = 0; t <= lastt; t++) {
			int h = hsh(p, t);
			int dir = get_last_move(h).d;
			mv.push_back(dir);
			p = p + dxy[dir];
		}
		for (int t = lastt; t >= time; t--) {
			int h = hsh(p, t);
			re_move(h, r, mv[t]);
			if (mv[t]) {
				moves++;
			}
			if (t > 0)
				p = p - dxy[mv[t - 1]];
		}
		robots[r] = hsh(p, time);
		return moves;
	}

	// memoized robotless distance function
	vector<array<array<short, DIM>, DIM>> mem_dist;
	inline int dist(pt p, int r) {
		// if (mem_dist[r][p.x+OFF][p.y+OFF] != INF)
		return mem_dist[r][p.x + OFF][p.y + OFF];
	}
	array<pt, DIM * DIM> q;
	void compute_dist(int r) {
		auto& dist = mem_dist[r];
		for (auto& v : dist) {
			v.fill(SINF);
		}
		pt start = ins.target[r];
		dist[start.x + OFF][start.y + OFF] = 0;
		int qs = 0, qt = 0;
		q[qt++] = start;
		while (qs < qt) {
			pt p = q[qs++];
			for (int i = 1; i <= 4; i++) {
				pt np = p + dxy[i];
				if (is_obstacle(np))
					continue;
				if (dist[np.x + OFF][np.y + OFF] == SINF) {
					dist[np.x + OFF][np.y + OFF] = dist[p.x + OFF][p.y + OFF] + 1;
					q[qt++] = np;
				}
			}
		}
	}

	// checks if moving from p -> np at time t is valid (assumes moving robot is
	// not in the graph)
	bool valid_move(const pt& p, const pt& np, const int t,
									bool notsosure = false) {
		if (t + 1 == (int)maxt && !notsosure)
			return true;
		// check whether p->np is valid
		int ch = hsh(np, t);
		int nh = hsh(np, t + 1);
		pt dir = np - p;
		node gch = get_last_move(ch);
		node gnh = get_last_move(nh);
		if (gnh.r != 0) { // some other robot or obstacle is there at next time step
			return false;
		}
		if (gch.r != 0) {					 // some other robot or obstacle is there now
			if (dxy[gch.d] != dir) { // if its not going the same direction as us
				return false;
			}
		}
		// check whether something moving ->p is still valid
		nh = hsh(p, t + 1);
		gnh = get_last_move(nh);
		if (gnh.r != 0) { // some robot is following behind, into the same spot
			if (gnh.r == -1) {
				cerr << "wait how was this robot on an obstacle ??? " << endl;
				assert(false);
			}
			int otherd = -1;

			for (int d = 1; d < 5; d++) { // figure out where it came from
				if (get_last_move(hsh(p + dxy[d], t)).r == gnh.r) {
					otherd = opposite_dir[d];
				}
			}
			if (otherd == -1 || dir != dxy[otherd]) {
				return false;
			}
		}
		return true;
	}

	vector<array<array<short, DIM>, DIM>> dis;
	vector<array<array<char, DIM>, DIM>> pre;
	vector<pair<int, pt>> vis;
	bool find_best_randomized(int r, int startt = 0,
														bool ignore_regressions = true,
														bool false_means_fail = false, int MULT = 1) {
		if (dis.empty()) {
			dis.resize(MAXT);
			for (auto& v : dis) {
				memset(v.data(), SINF, sizeof(v));
			}
			pre.resize(MAXT);
			/*
			for (auto& v : pre) {
				memset(v.data(), -1, sizeof(v));
			}*/
		}
		int old_moves = remove_path(r, startt);
		// middle tuple is random
		vector<vector<int>> pq(40 * MAXT);
		/*
		priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>,
									 greater<tuple<int, int, int>>>
				pq;*/
		int start = robots[r];
		int target = hsh(ins.target[r], maxt - 1); // target hash
		pt s = get_p(start);
		dis[0][s.x + OFF][s.y + OFF] = dist(s, r) * MULT;
		pq[dis[0][s.x + OFF][s.y + OFF]].push_back(start);
		vis = {{0, s}};
		bool found = false;
		for (int curt = 0; curt < int(pq.size()); curt++) {
			shuffle(begin(pq[curt]), end(pq[curt]), rng);
			while (!pq[curt].empty()) {
				int h = pq[curt].back();
				pq[curt].pop_back();
				pt p = get_p(h);
				int t = get_t(h);
				if (p == ins.target[r] && is_last_move(h)) {
					target = hsh(ins.target[r], t);
					found = true;
					goto end;
				}
				if (dis[t][p.x + OFF][p.y + OFF] < curt)
					continue;
				int nt = t + 1;
				for (int dir = 0; dir < 5; dir++) {
					pt np = p + dxy[dir];
					int nh = hsh(np, nt);
					int ncost = curt + 1 + ((dir != 0) - dist(p, r) + dist(np, r)) * MULT;
					if (in_range(np) && dis[t + 1][np.x + OFF][np.y + OFF] > ncost &&
							valid_move(p, np, t)) {
						// assert(ncost < 2*MAXT);
						dis[t + 1][np.x + OFF][np.y + OFF] = ncost;
						vis.emplace_back(t + 1, np);
						pq[ncost].push_back(nh);
						pre[t + 1][np.x + OFF][np.y + OFF] = dir;
					}
				}
			}
		}
	end:
		int moves = 0;
		if (!found) {
			cerr << "NO PATH FOUND, PLEASE FIX YOUR SEED INSTANCE" << endl;
			return false;
		} else {
			// recover path backwards
			robots[r] = target;
			int cur = target;
			vector<int> mvs;
			while (cur != start) {
				pt p = get_p(cur);
				int t = get_t(cur);
				int pdir = pre[t][p.x + OFF][p.y + OFF];
				if (pdir)
					moves++;
				mvs.push_back(pdir);
				cur = hsh(p - dxy[pdir], t - 1);
			}
			reverse(mvs.begin(), mvs.end());
			mvs.push_back(0); // add a no-move at the end
			for (int d : mvs) {
				add_move(cur, r, d);
				pt p = get_p(cur);
				int t = get_t(cur);
				cur = hsh(p + dxy[d], t + 1);
			}
			if (startt == 0 && old_moves > moves) {
				// cerr << "Improved robot r = " << r << ", from " << old_moves
				// 		 << " moves to " << moves << " moves."
				// 		 << "(" << -old_moves + moves << ") for " << ins.name << endl;
			} else if (startt == 0 && old_moves < moves) {
				if (ignore_regressions) {
					// cerr << "Robot regression r = " << r << ", from " << old_moves
					// 		 << " moves to " << moves << " moves." << "(+" << -old_moves +
					// moves << ") " << endl;
				} else {
					cerr << "THIS IS BAD, FAILING robot r = " << r << ", from "
							 << old_moves << " moves to " << moves << " moves."
							 << "(+" << -old_moves + moves << ") " << endl;
					assert(false);
				}
			}
		}
		for (const auto& [t, p] : vis) {
			dis[t][p.x + OFF][p.y + OFF] = SINF;
		}
		return false_means_fail || old_moves != moves;
	}

	bool find_best(int r, int startt = 0, bool ignore_regressions = true) {
		const long long MULT = 10; // travelling has MULTx the cost of staying still
		int old_moves = remove_path(r, startt);
		priority_queue<pair<int, int>, vector<pair<int, int>>,
									 greater<pair<int, int>>>
				pq;
		unordered_map<int, long long> dis; // best distance
		unordered_map<int, int> pre;			 // previous move
		int start = robots[r];
		int target = hsh(ins.target[r], maxt - 1); // target hash
		dis[start] = dist(get_p(start), r) * MULT;
		pq.push({dis[start], start});
		bool found = false;
		while (!pq.empty()) {
			long long cost = pq.top().first;
			int h = pq.top().second;
			pt p = get_p(h);
			int t = get_t(h);
			if (p == ins.target[r] && is_last_move(h)) {
				target = hsh(ins.target[r], t);
				found = true;
				break;
			}
			pq.pop();
			if (dis[h] < cost)
				continue;
			int nt = t + 1;
			for (int dir = 0; dir < 5; dir++) {
				pt np = p + dxy[dir];
				int nh = hsh(np, nt);
				long long ncost =
						cost + 1 + ((dir != 0) - dist(p, r) + dist(np, r)) * MULT;
				if (in_range(np) && (!dis.count(nh) || dis[nh] > ncost) &&
						valid_move(p, np, t)) {
					dis[nh] = ncost;
					pq.push({dis[nh], nh});
					pre[nh] = dir;
				}
			}
		}
		int moves = 0;
		if (!found) {
			cerr << "NO PATH FOUND, PLEASE FIX YOUR SEED INSTANCE" << endl;
			return false;
		} else {
			// recover path backwards
			robots[r] = target;
			int cur = target;
			vector<int> mvs;
			while (cur != start) {
				pt p = get_p(cur);
				int t = get_t(cur);
				int pdir = pre[cur];
				if (pdir)
					moves++;
				mvs.push_back(pdir);
				cur = hsh(p - dxy[pdir], t - 1);
			}
			reverse(mvs.begin(), mvs.end());
			mvs.push_back(0); // add a no-move at the end
			for (int d : mvs) {
				add_move(cur, r, d);
				pt p = get_p(cur);
				int t = get_t(cur);
				cur = hsh(p + dxy[d], t + 1);
			}
			if (startt == 0 && old_moves > moves) {
				cerr << "Improved robot r = " << r << ", from " << old_moves
						 << " moves to " << moves << " moves."
						 << "(" << -old_moves + moves << ") for " << ins.name << endl;
			} else if (startt == 0 && old_moves < moves) {
				if (ignore_regressions) {
					// cerr << "Robot regression r = " << r << ", from " << old_moves
					// 		 << " moves to " << moves << " moves." << "(+" << -old_moves +
					// moves << ") " << endl;
				} else {
					cerr << "THIS IS BAD, FAILING robot r = " << r << ", from "
							 << old_moves << " moves to " << moves << " moves."
							 << "(+" << -old_moves + moves << ") " << endl;
					assert(false);
				}
			}
		}
		return old_moves != moves;
	}

	void update_instance() {
		cerr << "UPDATING INSTANCE " << endl;
		ins.moves.clear();
		vector<vector<int>> vv(maxt, vector<int>(ins.n, 0));
		for (int r = 0; r < ins.n; r++) {
			pt pos = ins.start[r];
			int lastt = get_t(robots[r]);
			for (int t = 0; t <= lastt; t++) {
				int d = get_last_move(hsh(pos, t)).d;
				vv[t][r] = d;
				pos = pos + dxy[d];
			}
		}
		vector<int> zeroes(ins.n, 0);
		// trim still frames
		for (auto& v : vv)
			if (v != zeroes)
				ins.moves.push_back(v);
		ins.time = ins.moves.size();
	}
};

} // namespace sparse_graph
back to top