jzoj1303骑士

说在前面:

这道题目是上次集训的时候,还没有改的,之前给低年级的同学讲课的时候,找题,无意中就找到了这一道,然后发现还是不算太难,那时候没做出来真是有点傻。不过其实这道题模型的转换还是十分巧妙的。

题目大意:
骑士 (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;
}

猜你喜欢

转载自blog.csdn.net/ganjingxian/article/details/79291311