[BZOJ]1085 骑士精神(SCOI2005)

  这种鲜明的玄学风格很明显就是十几年前的题目。

Description

  在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位。在任何时候一个骑士都能按照骑士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2,纵坐标相差为1的格子)移动到空位上。 给定一个初始的棋盘,怎样才能经过移动变成如下目标棋盘: 为了体现出骑士精神,他们必须以最少的步数完成任务。

  

Input

  第一行有一个正整数T,表示一共有N组数据。接下来有T个5×5的矩阵,0表示白色骑士,1表示黑色骑士,*表示空位。两组数据之间没有空行。

Output

  对于每组数据都输出一行。如果能在15步以内(包括15步)到达目标状态,则输出步数,否则输出-1。

Sample Input

  2
  10110
  01*11
  10111
  01001
  00000
  01011
  110*1
  01110
  01010
  00100

Sample Output

  7
  -1

HINT

  T<=10。

Solution

  拿到这道题应该没有别的想法吧,于是我们直接开始往搜索去想。

  由于步数最多只有15,所以最暴力的暴力的时间复杂度为O(T*25*815)。

  如果用A*算法解决这道题,估价函数为不符合答案的格子个数,如果剩余步数小于这个值,那么这个状态一定不能在15步内走到答案。

  复杂度似乎可以不用乘上25,所以理论时间复杂度上限为O(T*815)。

  所以A*算法就是这么神奇地通过该题的,网络上题解有很多,小C就不再赘述。

  都是玄学。

  所以小C还是介绍一下自己的折半搜索做法吧。

  从目标状态往前搜8步,用一个map把每个状态hash的答案存下来。

  从每个起始状态往后搜7步,对于每个状态到map查找并更新答案。

  理论时间复杂度O(25*88*log(state)+T*25*87*log(state))。

  加一个判重的剪枝,可以跑得飞快。

  map还可以用O(1)的,hash还可以动态维护。所以时间复杂度可以优化到O(88+T*87)。

  而且每个状态不是满的8种转移方式,实际的时间复杂度远远小于上限。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#define ll unsigned long long
using namespace std;
typedef char matr[6][6];
const int fx[8][2]={{1,2},{1,-2},{-1,2},{-1,-2},{2,1},{2,-1},{-2,1},{-2,-1}};
map <ll,int> mp;
matr c,d;
int cx,cy,dx,dy,ans,t;

inline int read()
{
    int n=0,f=1; char c=getchar();
    while (c<‘0‘ || c>‘9‘) {if(c==‘-‘)f=-1; c=getchar();}
    while (c>=‘0‘ && c<=‘9‘) {n=n*10+c-‘0‘; c=getchar();}
    return n*f;
}

ll geth(matr c,int x,int y)
{
    ll h=0;
    register int i,j;
    for (i=1;i<=5;++i)
        for (j=1;j<=5;++j) h=h*2+c[i][j]-‘0‘;
    h=h*5+x-1; h=h*5+y-1;
    return h;
}

bool check(int x,int y) {return x<1||x>5||y<1||y>5;}

void dfs1(int stp)
{
    ll h=geth(c,cx,cy);
    if (mp[h]&&mp[h]<=stp) return;
    mp[h]=stp;
    if (stp>8) return;
    register int i,ncx,ncy;
    for (i=0;i<8;++i)
    {
        ncx=cx+fx[i][0];
        ncy=cy+fx[i][1];
        if (check(ncx,ncy)) continue;
        swap(c[cx][cy],c[ncx][ncy]);
        swap(cx,ncx); swap(cy,ncy);
        dfs1(stp+1);
        swap(cx,ncx); swap(cy,ncy);
        swap(c[cx][cy],c[ncx][ncy]);
    }
}

void dfs2(int stp)
{
    ll h=geth(d,dx,dy);
    if (mp[h]) ans=min(ans,stp+mp[h]-2);
    if (stp>7) return;
    register int i,ndx,ndy;
    for (i=0;i<8;++i)
    {
        ndx=dx+fx[i][0];
        ndy=dy+fx[i][1];
        if (check(ndx,ndy)) continue;
        swap(d[dx][dy],d[ndx][ndy]);
        swap(dx,ndx); swap(dy,ndy);
        dfs2(stp+1);
        swap(dx,ndx); swap(dy,ndy);
        swap(d[dx][dy],d[ndx][ndy]);
    }
}

int main()
{
    register int i,j;
    t=read();
    for (i=1;i<=5;++i)
        for (j=1;j<=5;++j)
                 if (i<j||i==j&&i<=2) c[i][j]=‘1‘;
            else if (i>j||i==j&&i>=3) c[i][j]=‘0‘;
    cx=cy=3; dfs1(1);
    while (t--)
    {
        ans=16;
        for (i=1;i<=5;++i) scanf("%s",d[i]+1);
        for (i=1;i<=5;++i)
        {
            for (j=1;j<=5;++j)
                if (d[i][j]==‘*‘) {dx=i; dy=j; d[i][j]=‘0‘; break;}
            if (j<=5) break;
        }
        dfs2(1);
        printf("%d\n",ans==16?-1:ans);
    }
}

Last Word

  不过仔细想想玄学题目并不只是十几年前的专利啊,比如FJOI的D2T2……

时间: 2024-10-07 22:53:52

[BZOJ]1085 骑士精神(SCOI2005)的相关文章

A*算法详解 BZOJ 1085骑士精神

转载1:A*算法入门 http://www.cppblog.com/mythit/archive/2009/04/19/80492.aspx 在看下面这篇文章之前,先介绍几个理论知识,有助于理解A*算法. 启发式搜索:启发式搜索就是在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索直到目标.这样可以省略大量无畏的搜索路径,提到了效率.在启发式搜索中,对位置的估价是十分重要的.采用了不同的估价可以有不同的效果. 估价函数:从当前节点移动到目标节点的预估费用:这个估计

bzoj 1085骑士精神 迭代深搜

题目传送门 题目大意:给出一幅棋盘,问能否复原,中文题面,不做解释. 思路:第一次写迭代深搜的题目,这道题还是挺经典的.这道题的状态很明显的每多搜一层就是多八倍,非常的多,而且又是t组输入,所以必定有很多点是在深层次的,所以用迭代深搜,这就是很多组数据在很浅的层就得到了答案,不需要多做了,而有一些样例则是确实会重复计算(答案层次比较深的时候),但是此时浪费的时间和之前节约的时间已经不是一个数量级的了,故用迭代深搜,这也是迭代深搜的标志性功能. 但是光迭代深搜没有用,还需要一个估价函数来剪枝,这里

[bzoj] 1085 骑士精神 || ID-DFS

原题 找到最少的步数成为目标状态. IDDFS(限制层数的dfs)即可

Bzoj 1085: [SCOI2005]骑士精神 (dfs)

Bzoj 1085: [SCOI2005]骑士精神 题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1085 dfs + 剪枝. 剪枝方法: 1.每次交换只能改变一个位置.若发现之间相差的步数加上以前走的步数大于15的话,直接舍弃这一状态. 2.初始时,\(ans\)设为\(16\) 有了上面两个剪枝就A了. 照这节奏,SCOI2005就刷完了??? #include <iostream> #include <cstdio>

BZOJ 1085: [SCOI2005]骑士精神( IDDFS + A* )

一开始写了个 BFS 然后就 T 了... 这道题是迭代加深搜索 + A* ------------------------------------------------------------------------------ #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #define rep( i , n ) for( int i = 0 ; i

[BZOJ 1085] [SCOI2005] 骑士精神 [ IDA* 搜索 ]

题目链接 : BZOJ 1085 题目分析 : 本题中可能的状态会有 (2^24) * 25 种状态,需要使用优秀的搜索方式和一些优化技巧. 我使用的是 IDA* 搜索,从小到大枚举步数,每次 DFS 验证在当前枚举的步数之内能否到达目标状态. 如果不能到达,就枚举下一个步数,重新搜索,即使某些状态在之前的 DFS 中已经搜索过,我们仍然搜索. 并且在一次 DFS 中,我们不需要判定重复的状态. 在 IDA* 中,重要的剪枝优化是 估价函数 ,将一些不可能存在可行解的枝条剪掉. 如果估价函数写得

【BZOJ 1085】 [SCOI2005]骑士精神

Description 在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位.在任何时候一个骑士都能按照骑士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2,纵坐标相差为1的格子)移动到空位上. 给定一个初始的棋盘,怎样才能经过移动变成如下目标棋盘: 为了体现出骑士精神,他们必须以最少的步数完成任务. Input 第一行有一个正整数T(T<=10),表示一共有N组数据.接下来有T个5×5的矩阵,0表示白色骑士,1表示黑色骑士,*表示空位.两组数据之间没有

【BZOJ】【1085】【SCOI2005】骑士精神

IDA*算法 Orz HZWER A*+迭代加深搜索=IDA* 这题的估价相当于一个可行性剪枝,即如果当前走的步数s+未归位的点数>搜索深度k,则剪枝 1 /************************************************************** 2 Problem: 1085 3 User: Tunix 4 Language: C++ 5 Result: Accepted 6 Time:1388 ms 7 Memory:1272 kb 8 *********

bzoj1085 [SCOI2005]骑士精神

1085: [SCOI2005]骑士精神 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 2490  Solved: 1422[Submit][Status][Discuss] Description 在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位.在任何时候一个骑士都能按照骑士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2,纵坐标相差为1的格子)移动到空位上. 给定一个初始的棋盘,怎样才能经过移动