kings(骑士)解题报告

                                           kings(骑士)

                                                Time Limit5000 ms    Memory Limit131072 KBytes

Description

  用字符矩阵来表示一个8x8的棋盘,'.'表示是空格,'P'表示人质,'K'表示骑士。 每一步,骑士可以移动到他周围的8个方格中的任意一格。如果你移动到的格子中有人质(即'P'),你将俘获他。但不能移到出棋盘或当前是'K'的格子中。 请问最少要移动多少步骑士才能俘获所有的人质。

 

Input Format

  第一行一个整数N(<=5),表示有多少个棋盘。即多组测试数据。 每一组有8行,每行8个字符。字符只有'.',大写'P',大写'K'三种字符。'P'和'K'的个数范围都在[1,10]。

 

Output Format

  有N行,每行只一个整数,相应棋盘俘获全部人质所需要的最少步数。

 

Sample Input


P......P 
........ 
........ 
........ 
...KK... 
........ 
........ 
P......P 
.....P.P 
..K....P 
....K... 
..PP...P 
...K..KK 
........ 
K....... 
KP.K....

 

Sample Output

20 
9

 

Solution

    分析:1.p,k<=10考虑用状压

               2.每个骑士的路径独立互不影响(若路径相交必然不是最优解)

    基于以上两点,可以先求出每个骑士拯救集合S(状压2进制表示)的最小步数,然后通过这个求出前i个骑士拯救集合S的最小步数

#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x7f7f7f7f
using namespace std;

int T,p[11],k[11],d[65][65];
int f1[11][11][1<<10],f2[11][1<<10];

void ycl(){
    for (int xa=1;xa<=8;++xa)
     for (int ya=1;ya<=8;++ya)
      for (int xb=1;xb<=8;++xb)
       for (int yb=1;yb<=8;++yb){
              int x=(xa-1)*8+ya,y=(xb-1)*8+yb;
              int dex=abs(xa-xb),dey=abs(ya-yb);
              int t=min(dex,dey);
              d[x][y]=t+(dex-t)+(dey-t);
       }
}

int main(){
    ycl();
    for (scanf("%d",&T);T;--T){
        memset(f1,0x7f,sizeof f1);
        memset(f2,0x7f,sizeof f2);
        p[0]=k[0]=0;
        for (int i=1;i<=8;++i)
         for (int j=1;j<=8;++j){
             char c=getchar();
             while (c!='.'&&c!='P'&&c!='K') c=getchar();
             if (c=='P') p[++p[0]]=(i-1)*8+j;
             if (c=='K') k[++k[0]]=(i-1)*8+j;
         }
        for (int i=1;i<=k[0];++i){
            for (int j=1;j<=p[0];++j)
             f1[i][j][1<<(j-1)]=d[k[i]][p[j]];
            for (int s=1;s<(1<<p[0]);++s)
              for (int j=1;j<=p[0];++j)
               if (s&(1<<(j-1)))
                for (int l=1;l<=p[0];++l)
                 if (!(s&(1<<(l-1))))
                  f1[i][l][s|(1<<(l-1))]=min(f1[i][l][s|(1<<(l-1))],f1[i][j][s]+d[p[j]][p[l]]);
        }
        for (int i=1;i<=k[0];++i)
          for (int s=1;s<(1<<p[0]);++s)
           for (int j=1;j<=p[0];++j)
            f1[i][0][s]=min(f1[i][0][s],f1[i][j][s]);
        f2[0][0]=0;
        for (int i=1;i<=k[0];++i){
            f2[i][0]=0;
            for (int s=1;s<(1<<p[0]);++s){
                f2[i][s]=f2[i-1][s];
                for (int ss=0;ss<s;++ss)
                 if ((s|ss)==s&&(f2[i-1][ss]!=INF&&f1[i][0][s^ss]!=INF))
                 f2[i][s]=std::min(f2[i][s],f2[i-1][ss]+f1[i][0][s^ss]);
            }
        }
        printf("%d\n",f2[k[0]][(1<<p[0])-1]);
    }
}

以上是标解,下面提供一种(蒟蒻)的做法

  我们发现每个骑士只有两种状态:1.在初始位置 2.在某个人质的位置上

  每个人质有三种状态:1.没被拯救 2.已经被拯救 3.被拯救且有骑士在这个位置

  所以 用f[s1][s2]表示人质状态为s1,骑士的状态为s2的最少步数

  每次转移可以将s1中的骑士走出去拯救人质或将s2中在初始位置的骑士走出去拯救人质

#include<cstdio>
#include<cstring>
#include<algorithm>

short int o3[2][59050];
short int p[11],k[11];
short int T,A,B,ans,pw[10],d[65][65],f[1<<10][59050];

inline void dfs(int s1,int s2){
    if (f[s1][s2]+(p[0]-o3[1][s2]-o3[0][s2])>=ans) return;
    if (o3[1][s2]+o3[0][s2]==p[0]){
        ans=std::min(ans,f[s1][s2]);
        return;
    }
    for (int i=0;i<p[0];++i)
        if (!((s2/pw[i])%3)){
          for (int j=0;j<p[0];++j)
            if (((s2/pw[j])%3)==2){
               int t1=s1,t2=s2-pw[j]+pw[i]*2;
               if (f[t1][t2]>f[s1][s2]+d[p[j+1]][p[i+1]])
                f[t1][t2]=f[s1][s2]+d[p[j+1]][p[i+1]],dfs(t1,t2);;
             }
           for (int j=0;j<k[0];++j)
             if (s1&(1<<j)){
             int t1=s1^(1<<j),t2=s2+pw[i]*2;
             if (f[t1][t2]>f[s1][s2]+d[k[j+1]][p[i+1]])
              f[t1][t2]=f[s1][s2]+d[k[j+1]][p[i+1]],dfs(t1,t2);
           }
    }    
}

inline void csh(){
    memset(f,0x7f,sizeof f);
    p[0]=k[0]=0;
       for (int i=1;i<=8;++i)
        for (int j=1;j<=8;++j){
           char c=getchar();
        while (c!='.'&&c!='P'&&c!='K') c=getchar();
        if (c=='P') p[++p[0]]=(i-1)*8+j;
        if (c=='K') k[++k[0]]=(i-1)*8+j;
     }
    A=(1<<(k[0]))-1;
    f[A][0]=0;
    ans=d[k[1]][p[1]];
    for (int i=2;i<=p[0];++i) ans+=d[p[i-1]][p[i]];
}

inline void ycl(){
    for (int xa=1;xa<=8;++xa)
     for (int ya=1;ya<=8;++ya)
      for (int xb=1;xb<=8;++xb)
       for (int yb=1;yb<=8;++yb){
            int x=(xa-1)*8+ya,y=(xb-1)*8+yb,t;
            int dex=std::abs(xa-xb),dey=std::abs(ya-yb);
            t=std::min(dex,dey);
            d[x][y]=d[y][x]=t+(dex-t)+(dey-t);
       }
    int maxs=0;
    for(int i=0;i<10;++i) maxs+=pw[i]*2;
    for (int i=0;i<=maxs;++i)
        for (int j=0;j<10;++j)
         if ((i/pw[j])%3) 
            ++o3[(i/pw[j])%3-1][i];
}

int main(){
   pw[0]=1;
   for (int i=1;i<10;++i) pw[i]=pw[i-1]*3;
   ycl();
   for (scanf("%d",&T);T;--T){
          csh();
       dfs(A,0);
       printf("%d\n",ans);
   } 
}
原文地址:https://www.cnblogs.com/hyheng/p/7654649.html