[USACO10MAR]The Rock Game S题解

题目链接:[USACO10MAR]The Rock Game S。

题目

描述

在奶牛回家休息和娱乐之前,Farmer John 希望它们通过玩游戏获得一些智力上的刺激。游戏板由 NNN 个相同的孔组成,这些孔最初都是空的。一头母牛要么用石头盖住一个洞,要么揭开一个先前被盖住的洞。游戏状态的定义是哪些洞被石头覆盖,哪些洞没有覆盖。游戏的目标是让奶牛准确地到达每个可能的游戏状态一次,然后返回到所有洞都没有覆盖的状态。以下是他们其中一次游戏的示例(空的洞用 O 表示,用石头盖住的洞用 X 表示):
[USACO10MAR]The Rock Game S题解_第1张图片现在牛被卡玩不下去了!他们必须打开一个洞,不管他们打开哪个洞,他们都会到达一个他们已经到达的状态。例如,如果他们从第二个洞中取出岩石,他们将到达他们在时间 2 已经访问过的状态(X O X)。
[USACO10MAR]The Rock Game S题解_第2张图片

难点

本题最大的难点在于如何判断奶牛到达的某种状态是否在之前出现过。实际上,既然每个洞只有两种状态,那么我们可以用二进制中的0、1与之分别对应。这样的话,n个洞的每一种状态就可以用一个独一无二的二进制数来表示。我们再建立一个标记数组visit [ ] 来记录每个二进制数(可以先将其转化为十进制数)是否被访问过。例如OOX表示二进制数001,转换成十进制数就是1。

核心思路

这是一道典型的搜索题,可以利用DFS来做。
设共有n个洞,当前是第step步,把所有洞的状态叫做整体状态
除去初始时所有洞全空的状态,完成这个游戏一共需要2^n步
[USACO10MAR]The Rock Game S题解_第3张图片

  1. 判断当前是否是第2^n步。若是,说明已经找到了可行解,停止搜索,直接退出程序
    因为第2^n步也是所有洞全空,我们可以在最后直接打印出来

  2. 每到达一种整体状态,从第1个洞开始依次选取一个洞将其的状态取反,然后判断新的整体状态是否被访问过
    若访问过,则将这个洞的状态复原,然后继续选择下一个洞;
    若未访问过,将此整体状态标记为已访问过(visit [ ]=1),然后将这个整体状态存入数组记录下来,然后进行下一步搜索(DFS(step+1))

  3. 回溯时将刚才改动的洞的状态复原,将此整体状态标记为未访问过(visit [ ]=0)。

注意:刚开始要将第0步的全空状态标记为已访问过!!!(为什么???)

visit[0]=1;

AC代码

#include 
#include 

//以0代表O,1代表X
int number[15];
int visit[100000],n;//visit为标记数组,n为长度
char result[100000][15];//存储结果

int output()//输出函数
{
    int i,j;
    for(i=0;i<(1<<n)-1;i++)
    {
        for(j=0;j<n;j++)
        {
            printf("%c",result[i][j]);
        }
        printf("\n");
    }
    return 0;
}

int hexadecimal_conversion()//进制转换
{
    int sum=0,i;
    for(i=0;i<n;i++)
    {
        sum=sum*2+number[i];
    }
    return sum;
}

int DFS(int step)//step代表当前第几步
{
    int i,j;
    if(step==(1<<n))//因为最后一组全为O,所以当step等于2^n时,即说明已找到可行解
    {
        output();//输出结果
        for(i=0;i<n;i++)//打印最后一步的全空状态
    	{
        	printf("O");
    	}
        exit(0);//因为只输出1组结果,所以直接退出程序
    }
    for(i=0;i<n;i++)
    {
        number[i]=!number[i];//状态取反
        if(visit[hexadecimal_conversion()])//若此数访问过即代表此状态已出现过
        {
            number[i]=!number[i];//复原
            continue;//若重复直接进行下次循环
        }
        visit[hexadecimal_conversion()]=1;//标记
        for(j=0;j<n;j++)//储存结果
        {
            switch(number[j])
            {
                case 0:result[step-1][j]='O';break;
                case 1:result[step-1][j]='X';break;
            }
        }
        DFS(step+1);//继续搜索
        visit[hexadecimal_conversion()]=0;//标记复原
        number[i]=!number[i];//将刚才改动的洞的状态复原
    }
    return 0;

}

int main()
{
    int i;
    scanf("%d",&n);
    visit[0]=1;//刚开始要将全空状态标记为已访问过
    for(i=0;i<n;i++)//打印第0步的全空状态
    {
        printf("O");
    }
    printf("\n");
    DFS(1);//从第1步开始搜索
    return 0;
}

不当之处,敬请斧正……

你可能感兴趣的:(洛谷题解)