USACO Training "Camelot"
USACO Training: "Camelot"
(This was a hard problem)Problem Analysis
If the problem did not specify that the king needed to be picked up, this would be a simple all-pairs shortest pathes problem: find the point (r,c) such that sum(distance((r,c),(i-th knight row, i-th knight column))) is minimized.However, we need to take into account that the king needs to be picked up and ridden to (r,c) too!
Our first discovery:
The king can only take at most two steps.
Why? We notice that for any sequence of three (non-undoing) steps the king makes, the knight can do it in three (or sometimes even two) steps, so it would be inconstructive for a king to move three steps when a knight carrying it can.We try this method:
Choose an arbitrary point (i,j).
Add up all the distances between each knight and (i,j) - if no shortest path exsists, (i,j) is invalid.
For each position a king can be in with at most two moves (ki, kj) find i such that the value:
max(abs(ki-kr), abs(kj-kr))+max(distance between i-th knight and (ki,kj) + distance between (ki,kj) and (i,j) - distance between i-th knight and (i,j))
is minimized.
Add this minimal value to the previous sum.
Find (i,h) such that this sum is minimized.
The answer is this minimized sum.
Strategy
Since the edge weight is always 1, we can use a simple BFS on every node in the grid.Floyd-Warshall's is probably possible in theory, but the initialization (also known as making sure each cell connects to each other cell knightwise of it) makes it quite difficult.
Code
/*TASK: camelot
ID: qlstudi3
LANG: C++14
*/
#include <fstream>
#include <queue>
#include <cstring>
#include <vector>
using namespace std;
ifstream cin("camelot.in");
ofstream cout("camelot.out");
int kxd[8] = {-2, -1, 1, 2, 2, 1, -1, -2};
int kyd[8] = { 1, 2, 2, 1, -1, -2, -2, -1};
struct state
{
int r, c;
state(int r, int c) : r(r), c(c) { }
};
int dist[30][35][30][35];
vector<state> knights;
int maxr, maxc;
queue<state> que;
void bfs(int r, int c)
{
dist[r][c][r][c] = 0;
que.push(state(r, c));
while(!que.empty())
{
state p=que.front(); que.pop();
int pr=p.r, pc = p.c;
for(int d=0; d<8; d++)
{
int nr = pr + kxd[d], nc = pc + kyd[d];
if(0 <= nr && nr < maxr && 0 <= nc && nc < maxc && dist[nr][nc][r][c] > 9000)
{
que.push(state(nr, nc));
dist[nr][nc][r][c] = dist[pr][pc][r][c]+1;
}
}
}
}
int main()
{
memset(dist, 127, sizeof dist);
state king(-1, -1);
cin >> maxc >> maxr;
char kingr; cin >> kingr >> king.c;
king.r = kingr-'A'; king.c--;
char nkr; int nkc; int kc = 0;
while(cin >> nkr >> nkc) { knights.push_back(state(nkr-'A', nkc-1)); kc++; }
if(kc == 0) // ensure there is at least one knight n
{
cout << 0 << endl;
return 0;
}
for(int i=0; i<maxr; i++)
for(int j=0; j<maxc; j++)
bfs(i, j);
int best = 9000;
for(int i=0; i<maxr; i++)
for(int j=0; j<maxc; j++)
{
int tot = 0;
bool ok = true;
for(state k:knights)
{
if(dist[i][j][k.r][k.c] < 9000)
tot += dist[i][j][k.r][k.c];
else
{
ok = false;
break;
}
}
if(!ok) continue;
int kbest = 9000;
int rll = max(king.r-2, 0), rul = min(king.r+2, maxr-1);
int cll = max(king.c-2, 0), cul = min(king.c+2, maxc-1);
for(int nkr=rll; nkr<=rul; nkr++)
for(int nkc=cll; nkc<=cul; nkc++)
{
if(dist[i][j][nkr][nkc] > 9000) continue;
for(state k:knights)
if(dist[nkr][nkc][k.r][k.c] < 9000 && dist[i][j][k.r][k.c] < 9000)
kbest = min(kbest, max(abs(nkr-king.r), abs(nkc-king.c))+dist[nkr][nkc][k.r][k.c]+dist[i][j][nkr][nkc]-dist[i][j][k.r][k.c]);
}
best = min(best, tot+kbest);
}
cout << best << endl;
}
Comments
Post a Comment