[状压DP]JZOJ 1303 骑士

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

分析

观察到人质和骑士数量很少,而且距离可知,考虑状压DP

预处理距离即可

我们设f[i][j][k]表示i个骑士最后一个解救的人质是j,总共解救的人质状态为k的最小步数

转移方程易得:

f[i][j][k|(1<<j-1)]=Min{f[i][l][k] (l=1~pcnt,l∈k,j不属于k)

再设g[i][j]表示i个骑士解救的人质状态为j的最小步数

那么方程也易得

k为j的子集

g[i][j]=Min{g[i-1][k]+Min{f[i][1~pcnt][j^k]

然后这样会T,我们发现第二个Min可以在转移f时顺便搞出来,就没了

然后判子集是:(j^k)+k==j,我傻傻地用j&k==k……然后WA了一天

#include <iostream> 
#include <cstdio>
#include <memory.h>
#include <cmath>
using namespace std;
int n,mxn,pcnt,kcnt;
int p[11][2],k[11][2],dis[11][11];
int f[11][11][1<<11],g[11][1<<11],m[11][1<<11];

int main() {
    for (scanf("%d",&n);n;n--) {
        pcnt=kcnt=0;
        memset(f,0x0f,sizeof f);memset(g,0x0f,sizeof g);memset(m,0x0f,sizeof m);
        for (int i=1;i<=8;i++)
            for (int j=1;j<=8;j++) {
                char c;
                do {
                    scanf("%c",&c);
                }
                while (c!='.'&&c!='P'&&c!='K');
                if (c=='P') p[++pcnt][0]=i,p[pcnt][1]=j;
                if (c=='K') k[++kcnt][0]=i,k[kcnt][1]=j;
            }
        mxn=1<<pcnt;
        g[0][0]=0;
        for (int i=1;i<=kcnt;i++) m[i][0]=0;
        for (int i=1;i<pcnt;i++)
            for (int j=i+1;j<=pcnt;j++)
                dis[i][j]=dis[j][i]=max(abs(p[i][0]-p[j][0]),abs(p[i][1]-p[j][1]));
        for (int i=1;i<=kcnt;i++) {
            for (int j=1;j<=pcnt;j++)
                f[i][j][1<<j-1]=max(abs(k[i][0]-p[j][0]),abs(k[i][1]-p[j][1])),m[i][1<<j-1]=f[i][j][1<<j-1];
            for (int j=1;j<mxn;j++)
                for (int k=1;k<=pcnt;k++)
                    if (!(j&(1<<k-1)))
                        for (int l=1;l<=pcnt;l++)
                            if (j&(1<<l-1)) f[i][k][j|(1<<k-1)]=min(f[i][k][j|(1<<k-1)],f[i][l][j]+dis[l][k]),
                            m[i][j|(1<<k-1)]=min(m[i][j|(1<<k-1)],f[i][k][j|(1<<k-1)]);
            for (int j=0;j<mxn;j++)
                for (int k=0;k<=j;k++)
                    if ((j^k)+k==j&&j!=k)
                        g[i][j]=min(g[i][j],g[i-1][k]+m[i][j^k]);
                        else if (j==k) g[i][j]=min(g[i][j],g[i-1][k]);
        }
        printf("%d
",g[kcnt][mxn-1]);
    }
}
View Code
在日渐沉没的世界里,我发现了你。
原文地址:https://www.cnblogs.com/mastervan/p/10975734.html