1我们预处理一个数组dist[i][j][p][q]表示骑士从(i,j)到(p,q)要走的最短步数(直接用bfs即可)
2我们枚举每个点作为汇合点
3对于每个点第一种情况是,骑士不接国王走过去,国王自己走过去。第二种情况是有个骑士走过去接国王。
4第一种情况直接无脑加dist就行,对于第二种情况枚举每个骑士去接国王然后到汇合点所用步数,去最小值就行啦。
#include<iostream>
#include<queue>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define inf 0x3f3f3f3f
struct node{int r,c;}king,knight[1009];
queue<node> qu;
char ch[11];
int dist[51][51][51][51],visit[1009][1009],
dx[8]={1,2,2,1,-1,-2,-2,-1},
dy[8]={2,1,-1,-2,-2,-1,1,2};
int n,m,tot=0,ans=inf;
bool check(int x,int y){return (x>0&&x<=n&&y>0&&y<=m);}
void bfs(int p,int q)
{
memset(visit,0,sizeof(visit));visit[p][q]=1;dist[p][q][p][q]=0;qu.push((node){p,q});
while(!qu.empty())
{
node k=qu.front();qu.pop();
int step=dist[p][q][k.r][k.c];
for (int i=0;i<8;i++)
{
int x=k.r+dx[i],y=k.c+dy[i];
if (check(x,y)&&!visit[x][y])
{
visit[x][y]=1;
dist[p][q][x][y]=step+1;
qu.push((node){x,y});
}
}
}
}
void init(){for (int i=1;i<=n;i++)for (int j=1;j<=m;j++)for (int p=1;p<=n;p++)for (int q=1;q<=m;q++) dist[i][j][p][q]=inf;}
int main()
{
scanf("%d%d",&n,&m);
scanf("%s%d",&ch,&king.r);king.c=ch[0]-'A'+1;
while(~scanf("%s%d",&ch,&knight[++tot].r)) knight[tot].c=ch[0]-'A'+1;
init();
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++) bfs(i,j);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
int res=0;bool f=true;
for (int k=1;k<=tot;k++)
if (dist[knight[k].r][knight[k].c][i][j]!=inf) res+=dist[knight[k].r][knight[k].c][i][j];else {f=false;break;}
if (!f) continue;
ans=min(ans,res+max(abs(king.r-i),abs(king.c-j)));
for (int k=1;k<=tot;k++)
{
int temp=res-dist[knight[k].r][knight[k].c][i][j];
if (temp>=ans) continue;
for (int x=king.r-2;x<=king.r+2;x++)
for (int y=king.c-2;y<=king.c+2;y++)
if (check(x,y))ans=min(ans,temp+dist[x][y][i][j]+dist[x][y][knight[k].r][knight[k].c]+max(abs(king.r-x),abs(king.c-y)));
}
}
printf("%d\n",ans);
return 0;
}