题意:题意比较长,直接摘抄nocow上面的翻译(http://www.nocow.cn/index.php/Translate:USACO/camelot)
描述
很久以前,亚瑟王和他的骑士习惯每年元旦去庆祝他们的友谊。为了纪念上述事件,我们把这些是看作是一个有一人玩的棋盘游戏。有一个国王和若干个骑士被放置在一个由许多方格组成的棋盘上,没有两个骑士在同一个方格内。
这个例子是标准的8*8棋盘
国王可以移动到任何一个相邻的方格,从下图中黑子位置到下图中白子位置前提是他不掉出棋盘之外。
一个骑士可以从下图中黑子位置移动到下图中白子位置(走“日”字形) 但前提是他不掉出棋盘之外。
在游戏中,玩家可在每个方格上放不止一个棋子,假定方格足够大,任何棋子都不会阻碍到其他棋子正常行动。
玩家的任务就是把所有的棋子移动到同一个方格里——用最小的步数。为了完成这个任务,他必须按照上面所说的规则去移动棋子。另外,玩家可以选择一个骑士跟国王从他们两个相遇的那个点开始一起行动,这时他们按照骑士的行动规则行动,其他的单独骑士则自己一直走到集中点。骑士和国王一起走的时候,只算一个人走的步数。
写一个程序去计算他们集中在一起的最小步数,而且玩家必须自己找出这个集中点。当然,这些棋子可以在棋盘的任何地方集合。
[编辑]格式
PROGRAM NAME: camelot
INPUT FORMAT
(file camelot.in)
第一行: 两个用空格隔开的整数:R,C 分别为棋盘行和列的长。不超过26列,40行。
第二行..结尾: 输入文件包含了一些有空格隔开的字母/数字对,一行有一个或以上。第一对为国王的位置,接下来是骑士的位置。可能没有骑士,也可能整个棋盘都是骑士。行从1开始,列从大写字母A开始。
OUTPUT FORMAT
(file camelot.out)
单独一行表示棋子集中在一个方格的最小步数。
[编辑]SAMPLE INPUT
8 8 D 4 A 3 A 8 H 1 H 8
国王位置在D4。一共有四个骑士,位置分别是A3,A8,H1和H8。
[编辑]SAMPLE OUTPUT
10
SAMPLE OUTPUT ELABORATION
他们集中在B5。 骑士1: A3 - B5 (1步) 骑士2: A8 - C7 - B5 (2步) 骑士3: H1 - G3 - F5 - D4 (此时国王开始与这个骑士一起走) - B5 (4步) 骑士4: H8 - F7 - D6 - B5 (3步) 1 + 2 + 4 + 3 = 10步
解题思路:
- 此题涉及到最短路径算法和剪枝操作
- 用result[i][j] = k存储从节点i走到节点j所需要的最短路径长度为k。如果节点i的坐标为(x, y),则i = x * R + y
- 用king_x, king_y存储国王的位置,用(knights[i][0], knights[i][1])存储骑士i的位置
- 以所有的点(i, j)为源点,进行SPFA,得到这个点到所有点的最短路径长度,分别存在result[i * R + j][0...R * C -1]中
- 对于所有的点i,用sum保存集合所需要的总步数。首先加上所有的骑士直接到达该点的总路程,然后对于每一个骑士knight_position,列举骑士能够带上王的所有点pick_position(也就是棋盘上所有的点),计算这个骑士为了带上王而多走的路程temp = result[knight_position][pick_position] + GetDistance(pick_x, pick_y, king_x, king_y) + result[pick_position][i] - result[knight_position][i]。在列举过程中用min_more来保存temp的最小值(其中GetDistance计算国王从前面的坐标走到后面的坐标需要走的步数)。然后将这个最小值加到sum上,就得到了骑士和国王到达点i所需要的最短路径。
- 在循环进行5的时候用min保存sum的最小值,这个min就是最终的结果
- 如果直接进行5这样的循环,那么对于每个点i,都要对每个骑士列举棋盘上所有的点,这样会超时。于是在列举每一个点i的时候,首先加上所有的骑士直接到达i的总路程,如果已经比min大了,就直接continue,这样可以避免一部分计算
代码:
/*
ID: zc.rene1
LANG: C
PROG: camelot
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX_R 30
#define MAX_C 26
#define MAX 9999999
#define WINDOW_LEN 30
int R, C;
int *distance;
int *queue;
int *in_queue;
int **result;
void SPFA(int x, int y)
{
int i, j, m, n;
int x_cp = x;
int y_cp = y;
int head = 0, tail = -1;
for (i=0; i<R*C; i++)
{
distance[i] = MAX;
}
distance[x * R + y] = 0;
queue[++tail] = x * R + y;
memset(in_queue, 0, R * C * sizeof(int));
in_queue[x * R + y] = 1;
while (head <= tail)
{
i = queue[head++];
in_queue[i] = 0;
for (m=-2; m<=2; m++)
{
for (n=-2; n<=2; n++)
{
if ((m * n == 0) || (m == n) || (m + n == 0))
{
continue;
}
else
{
x = i / R;
y = i % R;
x += m;
y += n;
if ((x<0) || (x>=C) || (y<0) || (y>=R))
{
continue;
}
j = x * R + y;
if ((j >= 0) && (j < R * C))
{
if ((distance[i] + 1) < distance[j])
{
distance[j] = distance[i] + 1;
if (!in_queue[j])
{
queue[++tail] = j;
in_queue[j] = 1;
}
}
}
}
}
}
}
result[x_cp * R + y_cp] = (int *)malloc(R * C * sizeof(int));
memcpy(result[x_cp * R + y_cp], distance, R * C * sizeof(int));
}
int GetDistance(int x, int y, int king_x, int king_y)
{
int a = (x > king_x) ? (x - king_x) : (king_x - x);
int b = (y > king_y) ? (y - king_y) : (king_y - y);
if (a > b)
{
a -= b;
}
else
{
b -= a;
}
return a + b;
}
int main(void)
{
FILE *fin, *fout;
int king_x, king_y, knight_position, pick_position;
int knights[MAX_R * MAX_C][2];
int knights_num = 0;
char ch;
int i, j, m, n, x, y, pick_x, pick_y, temp;
int min = MAX, min_more, min_j, sum;
fin = fopen("camelot.in", "r");
fout = fopen("camelot.out", "w");
/*get the input*/
fscanf(fin, "%d %d\n", &R, &C);
distance = (int *)malloc(R * C * sizeof(int));
queue = (int *)malloc(R * C * sizeof(int));
in_queue = (int *)malloc(R * C * sizeof(int));
result = (int **)malloc(R * C * sizeof(int *));
fscanf(fin, "%c %d\n", &ch, &i);
king_x = ch - 'A';
king_y = i - 1;
while (fscanf(fin, "%c %d", &ch, &i) == 2)
{
knights[knights_num][0] = ch - 'A';
knights[knights_num][1] = i - 1;
knights_num++;
fscanf(fin, "%c", &ch);
}
for (i=0; i<C; i++)
{
for (j=0; j<R; j++)
{
SPFA(i, j);
}
}
for (i=0; i<R*C; i++)
{
sum = 0;
for (j=0; j<knights_num; j++)
{
knight_position = knights[j][0] * R + knights[j][1];
sum += result[knight_position][i];
}
if (sum >= min)
{
continue;
}
x = i / R;
y = i % R;
min_more = GetDistance(x, y, king_x, king_y);
for (j=0; j<knights_num; j++)
{
knight_position = knights[j][0] * R + knights[j][1];
for (pick_x=0; pick_x < C; pick_x++)
{
for (pick_y=0; pick_y < R; pick_y++)
{
if ((pick_x<0) || (pick_x>=C) || (pick_y<0) || (pick_y>=R))
{
continue;
}
pick_position = pick_x * R + pick_y;
temp = result[knight_position][pick_position] + GetDistance(pick_x, pick_y, king_x, king_y) + result[pick_position][i] - result[knight_position][i];
if (temp < min_more)
{
min_more = temp;
}
}
}
}
sum += min_more;
if (sum < min)
{
min = sum;
}
}
fprintf(fout, "%d\n", min);
return 0;
}