jzoj3383-太鼓达人【欧拉回路,位运算】

正题


大意

一个数K,求一个最长的01环形序列(头和尾相连),使得每个长度为k的连续子序列都不相同。(要输出这个串,如果有多个答案输出字典序最小的)


解题思路

尝试将长度为k的01序列全排列一下我们会发现总共有 2n 2 n 种排列,那么其实这个序列长度很明显就是 2n 2 n 。然后我们开始想一想如何输出队列。
首先每个序列只能也必须出现一次,而每个序列后面都可以接上某些序列,而最后又得回到最开始的序列。这么一看,其实很像欧拉回路。所以我们可以用欧拉回路来求,将每个排列作为一个点,然后可以相接的连边。
连边方式:
首先我们可以发现其实这个排列可以连接的下一个排列只有两种情况,就是将 k2 k ∼ 2 的数取出来,然后在末尾加入一个 0/1 0 / 1
之后暴力欧拉回路


代码

#include<cstdio>
#include<algorithm>
#define K 2060
using namespace std;
int ans[K],n,k,m;
bool v[K];
bool euler(int x,int y)//求欧拉回路
{
    if (v[x]) return 0;
    ans[y]=x&1;//取二进制第一位
    v[x]=true;//标记
    if (y==n) return 1;
    if (euler((x<<1)&m,y+1)) return 1;//按字典序小的开始搜索
    if (euler(((x<<1)|1)&m,y+1)) return 1;//搜索
    v[x]=false;//回溯
}
int main()
{
    scanf("%d",&k);n=1<<k;m=n-1;
    printf("%d ",n);
    euler(n-2,1);//从n-2位保证输出的时候前面k个都是0
    for (int i=1;i<=n;i++)
      printf("%d",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值