#include #include #include #include #include #include #include using namespace std; const long long INF = (1LL << 60); // Solution config const int MAX_OPS = 200000000; const int MAX_CAKES = 25; const int MAX_PARTIES = 101; const int MAX_CONTINUATIONS = 20; // Problem constants const int MAX_N = 202; const int MAX_K = 202; const int MAX_P = 100002; struct Cell { int row, col; Cell(int row_ = 0, int col_ = 0) : row(row_), col(col_) {} }; struct Party { Cell pos; int start, end; bool operator < (const Party& r) const { return end != r.end ? end < r.end : start < r.start; } }; int n, p, k; int height[MAX_N][MAX_N]; Party parties[MAX_P]; Cell stores[MAX_K]; int wth[MAX_PARTIES][MAX_CAKES][3]; long long dyn[MAX_PARTIES][MAX_CAKES]; Cell inter[MAX_PARTIES][MAX_PARTIES]; vector cont[MAX_PARTIES]; vector last[MAX_N][MAX_N]; vector cost[MAX_N][MAX_N]; char dchar[4] = { 'U', 'R', 'D', 'L' }; int dir[4][2] = { {-1, 0}, {0, 1}, {1, 0}, {0, -1} }; string answer; long long cakeParts[MAX_CAKES]; mt19937 mt; int random() { int ret = mt(); return ret < 0 ? -ret : +ret; } long long getScore(long long time, int idx, int cakes, int rem) { long long numCakes = cakeParts[cakes] - cakeParts[rem]; long long stay = min(parties[idx].end - time + 1, parties[idx].end - parties[idx].start + 1LL); return stay * (numCakes + 1); } long long getTime(int from, Cell cur, int cakes, bool printPath, string& path) { long long ret = 0; /* fprintf(stderr, " >> want to go from cell (%d, %d) to (%d, %d) with %d cakes.\n", parties[from].pos.row, parties[from].pos.col, cur.row, cur.col, cakes); */ while (last[cur.row][cur.col][from] != -1) { if (printPath) path += dchar[last[cur.row][cur.col][from]]; /* fprintf(stderr, " -- at cell (%d, %d) with last = %d\n", cur.row, cur.col, last[cur.row][cur.col][from]); system("pause"); */ Cell nxt = Cell(cur.row - dir[last[cur.row][cur.col][from]][0], cur.col - dir[last[cur.row][cur.col][from]][1]); long long diff = abs(height[cur.row][cur.col] - height[nxt.row][nxt.col]); ret += (diff + cakeParts[cakes]) * (diff + cakeParts[cakes]) + 1; cur = nxt; } reverse(path.begin(), path.end()); return ret; } long long getTimeCakes(int from, int to, int cakes, int newCakes, bool printPath, string& path) { long long ret = 0; string part1, buy, part2; ret += getTime(from, inter[from][to], cakes, printPath, part1); ret += getTime(to, inter[from][to], cakes + newCakes, printPath, part2); reverse(part2.begin(), part2.end()); for (int i = 0; i < (int)part2.size(); i++) { if (part2[i] == 'U') part2[i] = 'D'; else if (part2[i] == 'D') part2[i] = 'U'; else if (part2[i] == 'R') part2[i] = 'L'; else if (part2[i] == 'L') part2[i] = 'R'; } if (printPath && newCakes > 0) { char buff[32]; sprintf(buff, "%d", (int)(cakeParts[cakes + newCakes] - cakeParts[cakes])); buy = string(buff); } if (printPath) { path = part1 + buy + part2; } return ret; } long long recurse(int idx, int cakes) { /* fprintf(stderr, "At cell (%d, %d) with %d cakes.\n", parties[idx].pos.row, parties[idx].pos.col, cakes); */ if (dyn[idx][cakes] != -1) { return dyn[idx][cakes]; } string tmp; long long ans = 0; for (int i = 0; i < (int)cont[idx].size(); i++) { int nxt = cont[idx][i]; long long time = parties[idx].end + getTime(idx, parties[nxt].pos, cakes, false, tmp); /* fprintf(stderr, " >> evaluating going to (%d, %d) at time %lld\n", parties[nxt].pos.row, parties[nxt].pos.col, time0); */ if (time < parties[nxt].end) { for (int rem = 0; rem <= cakes; rem++) { long long cur = recurse(nxt, rem) + getScore(time, nxt, cakes, rem); if (ans < cur) { ans = cur; wth[idx][cakes][0] = nxt; wth[idx][cakes][1] = rem; wth[idx][cakes][2] = 0; } } } for (int newCakes = 1; cakes + newCakes < MAX_CAKES; newCakes++) { long long time = parties[idx].end + getTimeCakes(idx, nxt, cakes, newCakes, false, tmp); if (time < parties[nxt].end) { for (int rem = 0; rem <= cakes + newCakes; rem++) { long long cur = recurse(nxt, rem) + getScore(time, nxt, cakes + newCakes, rem); if (ans < cur) { ans = cur; wth[idx][cakes][0] = nxt; wth[idx][cakes][1] = rem; wth[idx][cakes][2] = newCakes; } } } } } return dyn[idx][cakes] = ans; } void restorePath() { answer = ""; int idx = 0, cakes = 0; while (wth[idx][cakes][0] != -1) { int nxt = wth[idx][cakes][0]; int rem = wth[idx][cakes][1]; int newCakes = wth[idx][cakes][2]; int used = cakeParts[cakes + newCakes] - cakeParts[rem]; long long time = -1; if (newCakes == 0) { string tmp; time = parties[idx].end + getTime(idx, parties[nxt].pos, cakes, true, tmp); answer += tmp; } else { string tmp; time = parties[idx].end + getTimeCakes(idx, nxt, cakes, newCakes, true, tmp); answer += tmp; } /* fprintf(stderr, "Arrived at party %d (%d, %d) at time %lld, buying %d, giving %d cakes.\n", nxt, parties[nxt].pos.row, parties[nxt].pos.col, time, newCakes, use); */ answer += '+'; if (used > 0) { char buff[32]; sprintf(buff, "%d", used); answer += string(buff); } idx = nxt, cakes = rem; } } void calcDistBFS(int idx) { queue q; Cell origin = parties[idx].pos; // Without cakes on the way q.push(origin); cost[origin.row][origin.col][idx] = 0; last[origin.row][origin.col][idx] = -1; while (!q.empty()) { Cell cur = q.front(); q.pop(); for (int i = 0; i < 4; i++) { Cell nxt = Cell(cur.row + dir[i][0], cur.col + dir[i][1]); if (nxt.row == origin.row && nxt.col == origin.col) continue; if (nxt.row < 0 || nxt.row >= n || nxt.col < 0 || nxt.col >= n) continue; if (last[nxt.row][nxt.col][idx] == -1) { cost[nxt.row][nxt.col][idx] = cost[cur.row][cur.col][idx] + 1; last[nxt.row][nxt.col][idx] = i; q.push(nxt); } } } } void calcDistDijkstra(Cell origin) { } void calcPaths() { for (int row = 0; row < n; row++) { for (int col = 0; col < n; col++) { last[row][col].clear(), last[row][col].resize(p + 1, -1); cost[row][col].clear(), cost[row][col].resize(p + 1, INF); } } for (int idx = 0; idx <= p; idx++) { /* fprintf(stderr, " >> calculating paths from party %d...\n", idx); */ calcDistBFS(idx); } vector used(p + 1, -1); // P * NEXT * MAX_CAKES * MAX_CAKES < MAX_OPS int numNext = MAX_OPS / MAX_CAKES / MAX_CAKES / p; numNext = MAX_CONTINUATIONS; for (int idx = 0; idx < p; idx++) { for (int iter = 0; iter < numNext * 2 && (int)cont[idx].size() < numNext; iter++) { int nxt = idx + random() % (p - idx) + 1; if (used[nxt] == idx) continue; used[nxt] = idx; cont[idx].push_back(nxt); /* fprintf(stderr, " >> adding continuation from %d to %d\n", idx, nxt); */ } } // Get intermediate store for (int idx = 0; idx <= p; idx++) { /* fprintf(stderr, " >> getting intermediate store from party %d\n", idx); */ for (int i = 0; i < (int)cont[idx].size(); i++) { long long best = INF; int nxt = cont[idx][i]; for (int s = 0; s < k; s++) { long long cur = 0; cur += cost[stores[s].row][stores[s].col][idx]; cur += cost[stores[s].row][stores[s].col][nxt]; if (best > cur) { best = cur; inter[idx][nxt] = stores[s]; } } } } } void solve() { unsigned sTime = clock(); for (int i = 0; i < MAX_CAKES / 2; i++) cakeParts[i] = i; for (int i = MAX_CAKES / 2; i < MAX_CAKES; i++) cakeParts[i] = min(100000LL, cakeParts[i - 1] * 2); // Leave only part of the parties which we can evaluate in time // N * N * P * 20 <= MAX_OPS random_shuffle(parties + 1, parties + p + 1); p = min(p, min(MAX_PARTIES - 1, MAX_OPS / 20 / n / n)); sort(parties + 1, parties + p + 1); // fprintf(stderr, "Remaining parties: %d\n", p); calcPaths(); // fprintf(stderr, "Finished calculating paths.\n"); /* Cell origin = parties[0].pos; Cell destination = parties[1].pos; int cakes = 0, newCakes = 2; long long ans = getTimeCakes(0, 1, cakes, newCakes, true); fprintf(stderr, "Path from (%d, %d) to (%d, %d) with %d cakes and %d new cakes: %lld\n", origin.row, origin.col, destination.row, destination.col, cakes, newCakes, ans); */ memset(dyn, -1, sizeof(dyn)); memset(wth, -1, sizeof(wth)); long long ans = recurse(0, 0); // fprintf(stderr, "Found answer with score %lld\n", ans); restorePath(); } void getCell(FILE* in, Cell& cell) { fscanf(in, "%d %d", &cell.row, &cell.col); cell.row--, cell.col--; } void printOutput() { FILE* out = stdout; out = fopen("party.out", "wt"); fprintf(out, "%s\n", answer.c_str()); fclose(out); } void readInput() { FILE* in = fopen("party.in", "rt"); fscanf(in, "%d %d %d", &n, &p, &k); for (int row = 0; row < n; row++) { for (int col = 0; col < n; col++) { fscanf(in, "%d", &height[row][col]); } } getCell(in, parties[0].pos); parties[0].start = parties[0].end = 0; for (int i = 1; i <= p; i++) { getCell(in, parties[i].pos); fscanf(in, "%d %d", &parties[i].start, &parties[i].end); parties[i].end += parties[i].start - 1; } for (int i = 0; i < k; i++) { getCell(in, stores[i]); } fclose(in); } int main(void) { readInput(); solve(); printOutput(); return 0; }