说在前面:
这道题目是上次集训的时候,还没有改的,之前给低年级的同学讲课的时候,找题,无意中就找到了这一道,然后发现还是不算太难,那时候没做出来真是有点傻。不过其实这道题模型的转换还是十分巧妙的。
题目大意:
骑士 (Standard IO)
Time Limits: 5000 ms Memory Limits: 131072 KB
Description
用字符矩阵来表示一个8x8的棋盘,’.’表示是空格,’P’表示人质,’K’表示骑士。
每一步,骑士可以移动到他周围的8个方格中的任意一格。如果你移动到的格子中有人质(即’P’),你将俘获他。但不能移到出棋盘或当前是’K’的格子中。
请问最少要移动多少步骑士才能俘获所有的人质。
Input
第一行一个整数N(<=5),表示有多少个棋盘。即多组测试数据。
每一组有8行,每行8个字符。字符只有’.’,大写’P’,大写’K’三种字符。’P’和’K’的个数范围都在[1,10]。
Output
有N行,每行只一个整数,相应棋盘俘获全部人质所需要的最少步数。
Sample Input
输入1:
1
.PPPPKP.
……..
……..
……..
……..
……..
……..
……..
输入2:
2
P……P
……..
……..
……..
…KK…
……..
……..
P……P
…..P.P
..K….P
….K…
..PP…P
…K..KK
……..
K…….
KP.K….
Sample Output
输出1:
6
输出2:
20
9
Data Constraint
题解
分析一下题目给我们的模型,让我们用一堆骑士,去抓一堆的人质,是不是十分无脑,连搜索都难以下手,我们尝试去转换一下模型。
大胆尝试
对于一个骑士,假如他要到一个有骑士的格子里面,我们先让原来在这个格子中的骑士先走出来,然后再这个要走进去的骑士走进去。
转换模型I
原模型:用一堆骑士,去抓一堆的人质
一次转换:因为这一堆人质总是被一个个骑士分别俘获,所以我们可以把模型转换成用一个个骑士去俘获人质。
所以我们就得到了第一次转换的结果。
大开脑洞
假设我们当前这个骑士去俘获了第一个人质,之后假如这个骑士要去俘获其他人质,我们可以想象是这第一个被这个骑士俘获的人质去俘获其他的人质。
转换模型II
既然我们现在已经大开了脑洞,就应该比较好理解,那么模型就转换成,有一堆人质去俘获人质。
转换模型III
类比一下我们第一次转化模型,用一堆骑士,去抓一堆的人质,到一个个骑士去俘获一堆人质,那么我们是不是也可以一样的转换,有一堆人质去俘获一堆人质,到一个个人质去俘获一堆人质,答案是显然的。这样子我们就可以成功转换模型。
题解
disi,j表示i这个格子到j这个格子的切比雪夫距离。
设gi,j表示第i个人质,俘获人质的状态为j,且包括i。
状态转移方程:
gi,j=min{dis[i,x]+g[x,j-{i}]}x,i属于j
设fi,j表示第i个骑士,俘获人质的状态为j。
状态转移方程:
fi,j=min{dis[i,x],g[x,j]}x属于j
设hi,j表示前i个骑士,俘获人质状态为j
hi,j=min{hi-1,x+f[i,j-x]},x属于j。
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define fo(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)
const int N=500000;
using namespace std;
char ch[70][70];
int p1[70],p2[70],k1[70],k2[70],lp,lk,dis1[70][70],dis2[70][70],f[70][1050],g[70][1050],h[70][1050];
int main()
{
//freopen("jzoj1303.in","r",stdin);
//freopen("jzoj1303.out","w",stdout);
int T;
scanf("%d",&T);
while (T--)
{
lp=lk=0;
memset(p1,0,sizeof(p1));
memset(p2,0,sizeof(p2));
memset(k1,0,sizeof(k1));
memset(k2,0,sizeof(k2));
fo(i,1,8)
{
scanf("\n");
fo(j,1,8)
{
scanf("%c",&ch[i][j]);
if (ch[i][j]=='P')
{
p1[++lp]=i;
p2[lp]=j;
}
if (ch[i][j]=='K')
{
k1[++lk]=i;
k2[lk]=j;
}
}
}
memset(dis1,0,sizeof(dis1));
memset(dis2,0,sizeof(dis2));
memset(g,N,sizeof(g));
memset(f,N,sizeof(f));
memset(h,N,sizeof(h));
fo(i,1,lp)
fo(j,1,lp)
dis1[i][j]=max(abs(p1[i]-p1[j]),abs(p2[i]-p2[j]));
fo(i,1,lk)
fo(j,1,lp)
dis2[i][j]=max(abs(k1[i]-p1[j]),abs(k2[i]-p2[j]));
int maxset=(1<<lp)-1;
fo(i,0,lp-1)
g[i+1][1<<i]=0;
fo(i,0,lk)
f[i][0]=h[i][0]=0;
fo(j,0,maxset)
fo(i,1,lp)
if((1<<i-1)&j)
fo(x,1,lp)
if (x!=i&&(1<<x-1&j))
g[i][j]=min(g[i][j],g[x][j-(1<<i-1)]+dis1[i][x]);
fo(j,0,maxset)
fo(i,1,lk)
fo(x,1,lp)
if ((1<<x-1)&j)
f[i][j]=min(f[i][j],g[x][j]+dis2[i][x]);
fo(i,1,lk)
fo(j,0,maxset)
fo(x,0,maxset)
if ((x&j)==x)
h[i][j]=min(h[i][j],h[i-1][x]+f[i][j-x]);
printf("%d\n",h[lk][maxset]);
}
return 0;
}