Centuries ago, King Arthur and the Knights of the Round Table used to meet every year on New Year's Day to celebrate their fellowship. In remembrance of these events, we consider a board game for one player, on which one chesspiece king and several knight pieces are placed on squares, no two knights on the same square.
This example board is the standard 8x8 array of squares:

The King can move to any adjacent square from to
as long as it does
not fall off the board:

A Knight can jump from to
, as long as it does not fall off the board:

During the play, the player can place more than one piece in the same square. The board squares are assumed big enough so that a piece is never an obstacle for any other piece to move freely.
The player's goal is to move the pieces so as to gather them all in the same square - in the minimal number of moves. To achieve this, he must move the pieces as prescribed above. Additionally, whenever the king and one or more knights are placed in the same square, the player may choose to move the king and one of the knights together from that point on, as a single knight, up to the final gathering point. Moving the knight together with the king counts as a single move.
Write a program to compute the minimum number of moves the player must perform to produce the gathering. The pieces can gather on any square, of course.
PROGRAM NAME: camelot
INPUT FORMAT
Line 1: | Two space-separated integers: R,C, the number of rows and columns on the board. There will be no more than 26 columns and no more than 30 rows. |
Line 2..end: | The input file contains a sequence of space-separated letter/digit pairs, 1 or more per line. The first pair represents the board position of the king; subsequent pairs represent positions of knights. There might be 0 knights or the knights might fill the board. Rows are numbered starting at 1; columns are specified as upper case characters starting with `A'. |
SAMPLE INPUT (file camelot.in)
8 8 D 4 A 3 A 8 H 1 H 8
The king is positioned at D4. There are four knights, positioned at A3, A8, H1, and H8.
OUTPUT FORMAT
A single line with the number of moves to aggregate the pieces.SAMPLE OUTPUT (file camelot.out)
10
SAMPLE OUTPUT ELABORATION
They gather at B5.
Knight 1: A3 - B5 (1 move)
Knight 2: A8 - C7 - B5 (2 moves)
Knight 3: H1 - G3 - F5 - D4 (picking up king) - B5 (4 moves)
Knight 4: H8 - F7 - D6 - B5 (3 moves)
1 + 2 + 4 + 3 = 10 moves.
题目要求把一些骑士和一个国王移动到同一个地方,使得步数最少。。
还有一个点就是,其实可以去接国王,国王也是可以先动,然后让其实接的。
考虑不能接。
由于R,C<=30*26
(RC)^2暴力BFS直接求出各点之间的距离,注意有些点好像不能到,所以我这里贡献了一次WA:
2 2
A 1
B 2
................
然后呢?枚举所有可能的汇合点,然后让骑士去接。
哪些地方可能被接呢?
国王 | ||||||||
白色的地方(斜对角线都是),否则还是国王自己走划算。(当然国王可以一路走到ANS)
还有,不要把R和C搞反了。。
以下是代码,代码里RC搞反了,自己看吧。
/*
ID:cqz15311
LANG:C++
PROG:camelot
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
struct P{
int x,y;
}king,knight[32*32],q[32*32*2];
const int dx[8] = { 1, 2, 1, 2,-1,-2,-1,-2};
const int dy[8] = { 2, 1,-2,-1, 2, 1,-2,-1};
const int kwx[17] = {-2,-1,0,1,2,0,0, 0, 0,1,1,-1,-1,-2,-2,2, 2};
const int kwy[17] = { 0, 0,0,0,0,1,2,-1,-2,-1,1,-1,1, 2,-2,2,-2};
int dist[32][32][32][32];
int R,C,n;
int _x,_y,_xx,_yy;
int Ans;
using namespace std;
int main(){
freopen("camelot.in","r",stdin);
freopen("camelot.out","w",stdout);
scanf("%d%d",&C,&R);//这里不要看,一开始把R和C弄反了,为了方便就不改了
getchar();
scanf("%c%d",&king.x,&king.y);
king.x -= ('A'-1);
n = 1;
getchar();
while (~scanf("%c%d",&knight[n].x,&knight[n].y)){
knight[n].x-=('A'-1);
++n;
getchar();
}
--n;
memset(dist,-1,sizeof(dist));
for (int x=1;x<=R;x++){
for (int y=1;y<=C;y++){
int front,rear;
front = rear = 0;
q[rear] . x = x;
q[rear] . y = y;
rear++;
dist[x][y][x][y] = 0;
while (front < rear){
_x = q[front] . x;
_y = q[front] . y;
front ++;
for (int d=0;d<8;d++){
_xx = _x + dx[d];
_yy = _y + dy[d];
if ((_xx >= 1) && (_yy >= 1) && (_xx <= R) && (_yy <= C)){
if (dist[x][y][_xx][_yy] == -1) {
dist[x][y][_xx][_yy] = dist[x][y][_x][_y] + 1;
q[rear] . x = _xx;
q[rear] . y = _yy;
rear++;
// printf("(%d,%d,%d,%d):%d\n",x,y,_xx,_yy,dist[x][y][_xx][_yy]);
}
}
}
}
}
}
for (int i=1;i<=R;i++){
for (int j=1;j<=C;j++){
for (int k=1;k<=R;k++){
for (int l=1;l<=C;l++){
if (dist[i][j][k][l] == -1)
dist[i][j][k][l] = 0x3fffff;
}
}
}
}
//被这个数据坑了:
/*
2 26
A 1
Y 1
不是所有位置都能到达.
*/
Ans = 0x3fffffff;
for (int d = 0;d<17;d++){
//枚举国王可能先走的位置
_x = king.x + kwx[d];
_y = king.y + kwy[d];
if ((_x >= 1) && (_y >= 1) && (_x <= R) && (_y <= C)){
for (int tx = 1;tx <= R;tx ++){
for (int ty = 1;ty <= C;ty++){
//枚举终点
int Tot = 0;
for (int i = 1;i<=n;i++){
Tot += dist[knight[i].x][knight[i].y][tx][ty];
}
int Rec = Tot + max(abs(king.x - tx) , abs(king.y - ty));
//可能国王自己走
for (int i=1;i<=n;i++){
Rec = min(Rec,Tot - dist[knight[i].x][knight[i].y][tx][ty] +
dist[knight[i].x][knight[i].y][_x][_y] +
(max(abs(kwx[d]) , abs(kwy[d]))) +
dist[_x][_y][tx][ty]);
}
Ans = min(Ans,Rec);
}
}
}
}
printf("%d\n",Ans);
fclose(stdin);
fclose(stdout);
return 0;
}
/*
USER: Jack Chen [cqz15311]
TASK: camelot
LANG: C++
Compiling...
Compile: OK
Executing...
Test 1: TEST OK [0.000 secs, 8300 KB]
Test 2: TEST OK [0.000 secs, 8300 KB]
Test 3: TEST OK [0.000 secs, 8300 KB]
Test 4: TEST OK [0.014 secs, 8300 KB]
Test 5: TEST OK [0.070 secs, 8300 KB]
Test 6: TEST OK [0.112 secs, 8300 KB]
Test 7: TEST OK [0.000 secs, 8300 KB]
Test 8: TEST OK [0.000 secs, 8300 KB]
Test 9: TEST OK [0.042 secs, 8300 KB]
Test 10: TEST OK [0.196 secs, 8300 KB]
Test 11: TEST OK [0.000 secs, 8300 KB]
Test 12: TEST OK [0.000 secs, 8300 KB]
Test 13: TEST OK [0.000 secs, 8300 KB]
Test 14: TEST OK [0.000 secs, 8300 KB]
Test 15: TEST OK [0.000 secs, 8300 KB]
Test 16: TEST OK [0.000 secs, 8300 KB]
Test 17: TEST OK [0.000 secs, 8300 KB]
Test 18: TEST OK [0.000 secs, 8300 KB]
Test 19: TEST OK [0.000 secs, 8300 KB]
Test 20: TEST OK [0.000 secs, 8300 KB]
All tests OK.
*/
其实大家也可以参考这里的:
http://train.usaco.org/usacoanal2?a=2wpUpUrJQ28&S=camelot
就是
这是最短路径算法的修改。如果没有国王,则最短路径算法可以确定每个骑士必须行进到每个平方的距离。因此,在特定广场上收集的费用只是每个骑士必须旅行的距离的总和,这是相当简单的计算。
为了考虑国王,考虑一个骑士,在一个格子上“拿起”国王,然后去聚会。这只需要一些额外的动作,而不仅仅是前往聚会点。特别是国王必须移动到接收方,骑士必须前往这个格子,然后到达最后的聚集点。考虑这个骑士拿起国王的额外动作是“成本”的数量。改变最短路径算法是很简单的,以考虑通过用布尔标志来增加状态来提升国王,指示骑士是否具有国王。
在这种情况下,在特定位置收集的费用是每个骑士必须旅行到达该平方的距离的总和加上在途中骑士的最低成本。
因此,对于每个格子,我们保存两个数字,我们迄今为止看到的所有骑士的距离之和将不得不到达这个格子和其中一个骑士的最低成本方式(请注意,“拿起”国王的一个方法是让国王自己全程前往聚会地点)。然后,当我们得到一个新的骑士时,我们运行最短的路径算法,并将每个平方得到的骑士的成本(而不是拿起王牌)增加到那个位置的收集成本。另外,对于每个广场,我们检查新的骑士是否可以以比以前的骑士少的举动来拿起国王,并更新该值。
在所有的骑士都被处理完毕之后,我们确定所有正方形的最小成本,以达到这个平方加上一个骑士在接近那个平方的方式拿起王牌的额外费用。
这是什么意思呢?就是说预处理2次,第一次不带国王的到,第二次先去接国王,再到。这似乎是真的N^2了。强大
官方代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* "infinity"... > maximum distance possible (for one knight) */
#define MAXN 10400
/* maximum number of rows */
#define MAXR 40
/* maximum number of columns */
#define MAXC 26
/* cost of collecting all knights here */
int cost[MAXC][MAXR];
/* cost of getting a knight to collect the king */
int kingcost[MAXC][MAXR];
/* distance the king must travel to get to this position */
int kdist[MAXC][MAXR];
/* distance to get for current knight to get to this square */
/* third index: 0 => without king, 1 => with king */
int dist[MAXC][MAXR][2];
/* number of rows and columns */
int nrow, ncol;
int do_step(int x, int y, int kflag) {
int f = 0; /* maximum distance added */
int d = dist[x][y][kflag]; /* distance of current move */
/* go through all possible moves that a knight can make */
if (y > 0) {
if (x > 1)
if (dist[x-2][y-1][kflag] > d+1) {
dist[x-2][y-1][kflag] = d+1;
f = 1;
}
if (x < ncol-2) {
if (dist[x+2][y-1][kflag] > d+1) {
dist[x+2][y-1][kflag] = d+1;
f = 1;
}
}
if (y > 1) {
if (x > 0)
if (dist[x-1][y-2][kflag] > d+1) {
dist[x-1][y-2][kflag] = d+1;
f = 1;
}
if (x < ncol-1)
if (dist[x+1][y-2][kflag] > d+1) {
dist[x+1][y-2][kflag] = d+1;
f = 1;
}
}
}
if (y < nrow-1) {
if (x > 1)
if (dist[x-2][y+1][kflag] > d+1) {
dist[x-2][y+1][kflag] = d+1;
f = 1;
}
if (x < ncol-2) {
if (dist[x+2][y+1][kflag] > d+1) {
dist[x+2][y+1][kflag] = d+1;
f = 1;
}
}
if (y < nrow-2) {
if (x > 0)
if (dist[x-1][y+2][kflag] > d+1) {
dist[x-1][y+2][kflag] = d+1;
f = 1;
}
if (x < ncol-1)
if (dist[x+1][y+2][kflag] > d+1) {
dist[x+1][y+2][kflag] = d+1;
f = 1;
}
}
}
/* also check the 'pick up king here' move */
if (kflag == 0 && dist[x][y][1] > d + kdist[x][y]) {
dist[x][y][1] = d + kdist[x][y];
if (kdist[x][y] > f) f = kdist[x][y];
}
return f; /* 1 if simple knight move made, 0 if no new move found */
}
void calc_dist(int col, int row) {
int lv, lv2; /* loop variables */
int d; /* current distance being checked */
int max; /* maximum finite distance found so far */
int f; /* temporary variable (returned value from do_step */
/* initiate all positions to be infinite distance away */
for (lv = 0; lv < ncol; lv++)
for (lv2 = 0; lv2 < nrow; lv2++)
dist[lv][lv2][0] = dist[lv][lv2][1] = MAXN;
/* starting location is zero w/o king, kdist[col][row] with king */
dist[col][row][0] = 0;
max = dist[col][row][1] = kdist[col][row];
for (d = 0; d <= max; d++) { /* for each distance away */
for (lv = 0; lv < ncol; lv++)
for (lv2 = 0; lv2 < nrow; lv2++) {
/* for each position that distance away */
if (dist[lv][lv2][0] == d) {
/* update with moves through this square */
f = do_step(lv, lv2, 0);
if (d + f > max) /* update max if necessary */
max = d + f;
}
if (dist[lv][lv2][1] == d) {
/* same as above, except this time knight has king */
f = do_step(lv, lv2, 1);
if (d + f > max) max = d + f;
}
}
}
}
int main(int argc, char **argv) {
FILE *fout, *fin;
char t[10];
int pr, pc;
int lv, lv2;
int i, j;
if ((fin = fopen("camelot.in", "r")) == NULL) {
perror ("fopen fin");
exit(1);
}
if ((fout = fopen("camelot.out", "w")) == NULL) {
perror ("fopen fout");
exit(1);
}
fscanf (fin, "%d %d", &nrow, &ncol);
fscanf (fin, "%s %d", t, &pr);
pc = t[0] - 'A';
pr--;
/* Calculate cost of moving king from starting position to
* each board position. This is just the taxi-cab distance */
for (lv = 0; lv < ncol; lv++)
for (lv2 = 0; lv2 < nrow; lv2++) {
i = abs(pc-lv);
j = abs(pr-lv2);
if (i < j) i = j;
kingcost[lv][lv2] = kdist[lv][lv2] = i;
}
while (fscanf (fin, "%s %d", t, &pr) == 2) { /* for all knights */
pc = t[0] - 'A';
pr--;
/* calculate distances */
calc_dist(pc, pr);
for (lv = 0; lv < ncol; lv++)
for (lv2 = 0; lv2 < nrow; lv2++) {
/* to collect here, we must also move knight here */
cost[lv][lv2] += dist[lv][lv2][0];
/* check to see if it's cheaper for the new knight to
pick the king up instead of whoever is doing it now */
if (dist[lv][lv2][1] - dist[lv][lv2][0] < kingcost[lv][lv2]) {
kingcost[lv][lv2] = dist[lv][lv2][1] - dist[lv][lv2][0];
}
}
}
/* find best square to collect in */
pc = cost[0][0] + kingcost[0][0];
for (lv = 0; lv < ncol; lv++)
for (lv2 = 0; lv2 < nrow; lv2++)
if (cost[lv][lv2] + kingcost[lv][lv2] < pc) /* better square? */
pc = cost[lv][lv2] + kingcost[lv][lv2];
fprintf (fout, "%i\n", pc);
return 0;
}