Camelot 亚瑟王的宫殿
IOI 98
这个例子是标准的8*8棋盘
国王可以移动到任何一个相邻的方格,从下图中黑子位置到下图中白子位置前提是他不掉出棋盘之外。
一个骑士可以从下图中黑子位置移动到下图中白子位置(走“日”字形) 但前提是他不掉出棋盘之外。
在游戏中,玩家可在每个方格上放不止一个棋子,假定方格足够大,任何棋子都不会阻碍到其他棋子正常行动。
玩家的任务就是把所有的棋子移动到同一个方格里——用最小的步数。为了完成这个任务,他必须按照上面所说的规则去移动棋子。另外,玩家可以选择一个骑士跟国王从他们两个相遇的那个点开始一起行动,这时他们按照骑士的行动规则行动,其他的单独骑士则自己一直走到集中点。骑士和国王一起走的时候,只算一个人走的步数。
写一个程序去计算他们集中在一起的最小步数,而且玩家必须自己找出这个集中点。当然,这些棋子可以在棋盘的任何地方集合。
PROGRAM NAME: camelot
INPUT FORMAT
(file camelot.in)
第一行: 两个用空格隔开的整数:R,C 分别为棋盘行和列的长。不超过26列,30行。
第二行..结尾: 输入文件包含了一些有空格隔开的字母/数字对,一行有一个或以上。第一对为国王的位置,接下来是骑士的位置。可能没有骑士,也可能整个棋盘都是骑士。行从1开始,列从大写字母A开始。
OUTPUT FORMAT
(file camelot.out)
单独一行表示棋子集中在一个方格的最小步数。
8 8 D 4 A 3 A 8 H 1 H 8
国王位置在D4。一共有四个骑士,位置分别是A3,A8,H1和H8。
10
他们集中在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步
way:
处理出每个点到所有骑士的距离和e[u],然后 枚举 聚集的点u,枚举 骑士接国王的点v,枚举 哪个骑士k 去接国王,然后ans=min(e[u]-map[k][u]+map[k][v]+map[v][u]+dis(v)(国王到v移动的距离)).
map[u][v]指从u到v的最短距离BFS可以预处理,dis(v)指国王走到v的步数可以算,总复杂度O(n3)。
题解says 有两个优化加了可以过,一是如果当前e[u]已经大于ans剪掉,而是接国王的地方不会超过国王坐标+-2的范围(主要剪枝)(好险恶),证明不会……
代码:
/* ID:Andy Chen PROB:camelot LANG:C++ */ #include<iostream> #include<cstring> #include<cstdio> #include<cstdlib> #include<algorithm> using namespace std; const int dx[8]={-1,-2,-2,-1,1,2,2,1}; const int dy[8]={-2,-1,1,2,2,1,-1,-2}; struct node { int x,y,d; }q[10000],king,knight[800]; int n,m,tot; int map[800][800],e[800]; //map 表示(x1,y1) to (x2,y2) 的dis 只是把四维写成了二维而已.空间是一样的 char ch; bool b[800][800]; void BFS(int sx,int sy) { int h=1,t=1; q[1].x=sx,q[1].y=sy,q[1].d=0; memset(b,0,sizeof(b)); b[sx][sy]=1; while (h<=t) { for (int i=0;i<8;i++) { int xx=q[h].x+dx[i],yy=q[h].y+dy[i]; if (1<=xx && xx<=n && 1<=yy && yy<=m && !b[xx][yy]) { q[++t].x=xx; q[t].y=yy; q[t].d=q[h].d+1; b[xx][yy]=1; map[(sx-1)*m+sy][(xx-1)*m+yy]=q[t].d; } } h++; } } int dis(int x,int y)//计算 king从原本位置走到(x,y)的最小距离 { return min(abs(king.x-x),abs(king.y-y))+abs(abs(king.x-x)-abs(king.y-y)); //先让king斜着走 走来和(x,y)在一条直线 竖着或横着沿着直线走就最小 //min(abs(king.x-x),abs(king.y-y)) 计算的是直线部分 //abs(abs(king.x-x)-abs(king.y-y)) 计算的是斜着走部分 } int main() { freopen("camelot.in","r",stdin); freopen("camelot.out","w",stdout); scanf("%d%d\n",&n,&m); scanf("%c %d\n",&ch,&king.x); king.x=n-king.x+1,king.y=ch-'A'+1; while (scanf("%c %d\n",&ch,&knight[++tot].x)!=EOF) knight[tot].x=n-knight[tot].x+1,knight[tot].y=ch-'A'+1; tot--; memset(map,10,sizeof(map)); for (int i=1;i<=n*m;i++) map[i][i]=0; for (int i=1;i<=n;i++)//预处理 期盼中 按照knight方式 任意两点的最小距离。 for (int j=1;j<=m;j++) BFS(i,j); memset(e,0,sizeof(e));// 处理出所有knight去(sx,sy)的最小距离和 for (int sx=1;sx<=n;sx++) for (int sy=1;sy<=m;sy++) { int u=(sx-1)*m+sy; for (int i=1;i<=tot;i++) e[u]+=map[u][(knight[i].x-1)*m+knight[i].y]; } int ans=0x7fffffff; for (int sx=1;sx<=n;sx++) for (int sy=1;sy<=m;sy++) if (ans>e[(sx-1)*m+sy]) for (int i=max(1,king.x-2);i<=min(n,king.x+2);i++) for (int j=max(1,king.y-2);j<=min(m,king.y+2);j++) { int u=(sx-1)*m+sy,v=(i-1)*m+j; for (int k=1;k<=tot;k++) { int t=(knight[k].x-1)*m+knight[k].y; int temp=e[u]-map[u][t]+map[t][v]+map[v][u]+dis(i,j); ans=min(ans,temp); } } if (tot==0) ans=0; printf("%d\n",ans); return 0; }