hdu 2894 DeBruijin(暴力搜索 || 欧拉回路)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=2894

Problem Description
旋转鼓的表面分成m块扇形,如图所示(m=8)。图中阴影区表示用导电材料制成,空白区用绝缘材料制成,终端a、b和c是3(k=3)处接地或不是接地分别用二进制信号0或1表示。因此,鼓的位置可用二进制信号表示。试问应如何选取这8个扇形的材料使每转过一个扇形都得到一个不同的二进制信号,即每转一周,能得到000到111的8个数。


那我们现在把旋转鼓的表面分成m块扇形,每一份记为0或1,使得任何相继的k个数的有序组(按同一方向)都不同,对固定的k,m最大可达到多少,并任意输出符合条件的一个这样的有序组。

 

Input
每个case输入一个数k (2<=k<=11),表示图中所示的abc这样的接地线的数量。
 

Output
每个case输出m所能达到的最大值 ,并且输出字典序最小的一个符合条件的有序组,中间用空格隔开。Case间没有空行。有序组输出的格式为:00010111(k=3,只输出一个周期(0001011100010111……),并且首尾刚好是相接的)。
 

Sample Input
  
  
3
 

Sample Output
  
  
8 00010111
分析:刚开始使用搜索做的,后来在网上查了查别人的做法,发现还可以用欧拉回路解决,甚妙!
暴力:
/*
看到k的值不大,猜想和枚举搜索等相关
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=2100;  //2^11=2048
int k,num;
char str[maxn],s[15];
bool tag[maxn];
int get(){
    int ans=0,temp;
    if(s[k-1]=='1') ans+=1;
    temp=1;
    for(int i=k-2;i>=0;i--){
        temp*=2;
        if(s[i]=='1') ans+=temp;
    }
    return ans;
}
bool flag;
void dfs(char x,int top){
    strncpy(s,str+top-k+1,k);
    s[k-1]=x;
    int temp=get();
    if(!tag[temp]){
        tag[temp]=1;
        str[top]=x;
        if(top==num-1){  flag=1; return ; }
        //cout<<temp<<" "<<s<<" "<<str<<endl;
        dfs('0',top+1);
        dfs('1',top+1);
        if(flag) return ;
        tag[temp]=0;
        str[top]=0;
    }
}
int main()
{
    while(cin>>k){
        num=(1<<k);
        memset(tag,0,sizeof(tag));
        memset(s,0,sizeof(s));
        memset(str,0,sizeof(str));
        for(int i=0;i<k;i++){
            s[i]='0';
            str[i]='0';
        }
        tag[0]=1;
        flag=0;
        dfs('1',k);
        printf("%d %s\n",num,str);
    }
    return 0;
}

欧拉回路:
/*
圆环上一定有2^n个小段,把小段的两个端点看做各自一点,那么就有2^n个点。
这些点刚好形成一个欧拉回路。怎样由一个点寻找下一个点呢?设当前点是k,
那么和它各二进制位差异最小的相邻点就是(k<<1)&((1<<n)-1)或者(k<<1)&((1<<n)-1)+1.
0 000;
1 001;
2 010;
4 100;
5 101;
3 011;
6 110;
7 111.
如果在递归之前把01放进sta中,正序输出的结构应该是01001101。应该在dfs终止了
(一条路走不通了)才放进01信息(那样最先进入的一定是111……),最后逆序输出
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=2100;
bool vis[N];
int sta[N],top,k,num;
void dfs(int x){
    int q1=(x<<1)&((1<<k)-1),q2=q1+1;
    if(!vis[q1]){
        vis[q1]=1;
        //sta[++top]=0;
        dfs(q1);
        sta[++top]=0;
    }
    if(!vis[q2]){  //两个都是if 不是if,else if的关系
        vis[q2]=1;
        //sta[++top]=1;
        dfs(q2);
        sta[++top]=1;
    }
}
int main()
{
    while(cin>>k){
        num=(1<<k);
        top=0;
        memset(vis,0,sizeof(vis));
        dfs(0);
        printf("%d ",num);
        for(int i=1;i<k;i++)printf("0");
        for(int i=top;i>=k;i--) printf("%d",sta[i]);
        puts("");
        //for(int i=1;i<=top;i++) printf("%d",sta[i]);
        //printf("%d\n",sta[top-1]);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值