zoukankan      html  css  js  c++  java
  • 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);
       } 
    }
  • 相关阅读:
    9 Fizz Buzz 问题
    2 尾部的零
    1 A+B问题
    递归
    互斥同步
    垃圾收集器与内存分配策略---垃圾收集算法
    10.矩形覆盖
    9.变态跳台阶
    8.跳台阶
    9.path Sum III(路径和 III)
  • 原文地址:https://www.cnblogs.com/hyheng/p/7654649.html
Copyright © 2011-2022 走看看