[BZOJ3033]:太鼓达人(爆搜)
题目传送门
题目描述
七夕祭上,Vani牵着cl的手,在明亮的灯光和欢乐的气氛中愉快地穿行。这时,在前面忽然出现了一台太鼓达人机台,而在机台前坐着的是刚刚被精英队伍成员XLk、Poet_shy和lydrainbowcat拯救出来的的applepi。看到两人对太鼓达人产生了兴趣,applepi果断闪人,于是cl拿起鼓棒准备挑战。然而即使是在普通难度下,cl的路人本性也充分地暴露了出来。一曲终了,不但没有过关,就连鼓都不灵了。Vani十分过意不去,决定帮助工作人员修鼓。
(BZOJ废话总是不少)
鼓的主要元件是M个围成一圈的传感器。每个传感器都有开和关两种工作状态,分别用1和0表示。显然,从不同的位置出发沿顺时针方向连续检查K个传感器可以得到M个长度为K的01串。Vani知道这M个01串应该是互不相同的。而且鼓的设计很精密,M会取到可能的最大值。现在Vani已经了解到了K的值,他希望你求出M的值,并给出字典序最小的传感器排布方案。
输入格式
一个整数K。
输出格式
一个整数M和一个二进制串,由一个空格分隔。表示可能的最大的M,以及字典序最小的排布方案,字符0表示关,1表示开。你输出的串的第一个字和最后一个字是相邻的。
样例
样例输入:
3
样例输出:
8 00010111
数据范围与提示
得到的8个01串分别是000、001、010、101、011、111、110和100。注意前后是相邻的。长度为3的二进制串总共只有8种,所以M=8一定是可能的最大值。
对于全部测试点,2≤K≤11。
题意解释
一开始没有看懂题,在那里打表找规律……
语文不好是硬伤,得语文者得OI……
好吧,这道题就是说,让你找一个尽可能长的01环,让这个环不管从哪里开始,连续K位都不一样,要求找一个字典序尽可能小的。
题解
显然,长度为K的不同的01串共有${2}^{K}$个,那么第一问就完美解决了,直接输出${2}^{K}$就好了。
现在来考虑第二问,不要被其它题解所迷惑,别想的太复杂,好多人都说这道题是一个欧拉回路,不得不承认的确是的,但是你只要会爆搜就可以了。
要求字典序最小,所以前k位肯定是K个0。
然后看第K+1位,因为前面已经有K个连续的0了,所以这一位一定为1。
开始爆搜,每一次都枚举,先枚举0,再枚举1,用一个vis数组存储当前位的前K-1位和当前位的枚举值合在一起有没有出现过,将每一位压入栈,进行剪枝,搜到头即为答案(先是0后是1,保证了字典序最小)。
最后暴力弹栈输出结果。
然后说一些其他的:
A掉之后,旁边一个大佬跟我说,有规律,然后我在那里找了2个小时,没找出来,然后他跟我说,逗你玩呢……
但是还是有一些发现的,让我列举一下:
$\alpha$.试图把每K位断开,发现从第2组到第k组第一位都为1,剩下的K-1位按字典序从K个0开始。
$\beta$.最后的K个数都由1组成,那么第倒数K+1位数肯定是0,利用这个规律,我们可以完美解决最后一位和第一位连在一起之后代码实现较为困难的问题(其实就是我手懒)。
因为数据范围较小,所以可以打表。
当然,考试的时候建议进行打表,因为本地评测没有O2,而恰恰这道题的网站评测使用了O2,导致本地和网站评测输出不一样,疯狂WA。
需要注意的就是,评测环境为非O2的情况下,bool类型函数如果没有返回值自动返回0,而评测环境为O2的情况下返回值则为1。
代码时刻
#include<bits/stdc++.h> int k; bool vis[5000];//记录当前情况有没有出现过 int flag,flag1,flag2,top;//分别记录答案长度,还有几个0,还有几个1,当前长度 bool ans[5000];//记录答案 int dfs(int x,int y) { if(x>(1<<k))return 1; if(!vis[((y&flag)<<1)]&&flag1)//枚举0的情况 { flag1--; vis[((y&flag)<<1)]=1; if(dfs(x+1,((y&flag)<<1))){ans[++top]=0;return 1;} flag1++; vis[((y&flag)<<1)]=0; } if(!vis[(((y&flag)<<1)|1)]&&flag2)//枚举1的情况 { flag2--; vis[(((y&flag)<<1)|1)]=1; if(dfs(x+1,(((y&flag)<<1)|1))){ans[++top]=1;return 1;} flag2++; vis[(((y&flag)<<1)|1)]=0; } return 0; } int main() { scanf("%d",&k); switch(k)//第1问和前k+1位直接输出 { case 2 :printf("4 001");break; case 3 :printf("8 0001");break; case 4 :printf("16 00001");break; case 5 :printf("32 000001");break; case 6 :printf("64 0000001");break; case 7 :printf("128 00000001");break; case 8 :printf("256 000000001");break; case 9 :printf("512 0000000001");break; case 10:printf("1024 00000000001");break; case 11:printf("2048 000000000001");break; } flag=1<<(k-1); flag--; flag1=1<<(k-1); flag1-=k; flag2=flag; vis[0]=vis[1]=1;//标记两种情况已经出现过了 dfs(k+2,1); for(int i=top;i;i--)printf("%d",ans[i]);//输出 return 0; }
rp++