题解 P6183 【[USACO10MAR]The Rock Game S】
题意
求一个长度为 的 OX 串的全排列,并要求:
第一个排列必须全是 O。
除了最后一个排列外,每一个排列都不能重复。
相邻的排列只能有一个位置不一样。
遍历完所有排列后,还要能回到第一个排列。
分析
首先,看到数据范围 ,可以一眼看穿这就是个深搜,对于当前这个状态,不断修改他的每一位,标记为走过,再递归下去,因为第一个全是 O 的状态出现了两次,所以我们走到第一个状态时不需标记。然后可以发现,每个状态的每个位置都是由 O 或 X 组成,这不正是二进制吗?所以我们可以用一个整数来表示当前的状态,这个整数二进制状态下的每一位就代表一个 O 或 X,如代表 XOOX。接着,我们再考虑深搜时剪枝,如果这个状态前面已经有过了,就剪枝。
最后,我们得出了正确做法:
深搜 。
判断边界,如果每个状态都走了一遍,并回到起点,就输出,终止程序。
不断选择更改目前这个状态的每一位,如果这个状态没有走过,就标记这个状态,并存到答案的数组(记录每一步走的是哪个状态)中,把这个状态带到递归下去,然后别忘了回溯。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=(1<<15)+1;//最多有2^15种状态
int n,m,a[N];//n如题意,m为状态总数,a数组存储答案的每个状态
bool b,c[N]={1};//b用于终止程序,c数组存储每个状态是否走过,c[0]提前标记,不然n=2,3会错
void pr()//输出
{
for(int i=0;i<n;i++)cout<<"O";cout<<endl;//第一个没有标记的状态要另外输出
for(int i=m-1;i>=0;i--)
{
for(int j=n-1;j>=0;j--)
if((a[i]>>j)&1)printf("X");
else printf("O");
printf("\n");
}
}
void dfs(int w)//深搜,w表示已经有了几个状态
{
if(b==1)return;//如果已经输出了就终止其他的dfs
if(w==m)//如果走完了所有状态
{
if(a[m]==0)//如果最后又回到开始状态
{
pr();//输出
b=1;//标记为已经输出
}
return ;
}
int v;//v表示目前状态改变后的状态的值
for(int i=n-1;i>=0;i--)
{
if((a[w-1]>>i)&1)v=a[w-1]-(1<<i);//把O改成X或把X改成O
else v=a[w-1]+(1<<i);
if(!c[v])//如果修改后没有访问过
{
c[v]=1;a[w]=v;//标记并存入答案
dfs(w+1);//搜索下去
c[v]=0;a[w]=0;//回溯千万别忘了
}
}
}
int main()
{
cin>>n;
m=(1<<n);//总共有2^n种状态(不算第一种)
dfs(1);//第一种状态不搜索,因为已经确定,也不标记为走过
return 0;
}

浙公网安备 33010602011771号