杭电2894DeBruijin(欧拉回路)

->题目请戳这里<-

题目大意:一个旋转鼓,上面均匀分成m份,每一份可以表示二进制的0或1,现在求一个最短的01序列,使任意的k个相邻的01序列表示不同的值。并输出字典序最小的一个序列。

题目分析:这个序列是循环的,长度为k的二进制数表示的范围是0-2^k - 1,鼓转动一小格,当前k个二进制位与前一个状态相比,只有首尾2个二进制位发生了改变,中间的k-1位是不变的,考虑以这k-1位二进制位为点,建图,每个点伸出2条边,每条边的权值就是该点末尾补0 或1得到的值。举例说明一下,当k = 3的时候,有00,01,10,11,4种中间状态,就以这4个状态为点建图,00后面补1变成001,去掉高位后变成01,到达01这个状态,这条边的权值就是001,01后面补1变成011,到达11状态,这条边权值为011,同理,可以建2^3=8条边,可以发现这8条边各不相同,正好能表示0-2^k-1这2^k个数。于是这题就变成了求欧拉回路的问题。以2^(k - 1)个中间状态为点建图,建2^k条边,遍历这个图求一条欧拉回路即可。

详情请见代码:

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1<<12;
struct node
{
    int to;//边的右端点
    int value;//这条边的值
    int next;//同一个左端点的下一条边
    bool vis;//
}edge[N];
int head[N];
int ans,len,num,k;
int stack[N],top;

void init(int l)
{
    int i;
    for(i = 0;i < l;i ++)
        head[i] = -1;
    num = top = 0;
}

void build(int s,int e,int v)
{
    edge[num].to = e;
    edge[num].value = v;
    edge[num].vis = 0;
    edge[num].next = head[s];//后建的边往前插
    head[s] = num ++;
}

void dfs(int u)
{
    int i;
    for(i = head[u];i != -1;i = edge[i].next)
    {
        if(!edge[i].vis)
        {
            edge[i].vis = 1;
            dfs(edge[i].to);
            stack[top ++] = edge[i].value/len;
            //printf("%d",edge[i].value/len);
        }
    }
}

int main()
{
    int i;
    while(scanf("%d",&k) != EOF)
    {
        ans = 1<<k;
        len = 1<<(k - 1);
        init(len);
        for(i = 0;i < len;i ++)
        {
            int id = i<<1|1;//先建1边,再建0边
            build(i,id%len,id);//dfs的时候先搜
            id = i<<1;//权值小的边,保证字典序最小
            build(i,id%len,id);
        }
        printf("%d ",ans);
        dfs(0);
        for(i = top - 1;i >= 0;i --)
            printf("%d",stack[i]);
        printf("\n");
    }
    return 0;
}
//31MS	384K


你可能感兴趣的:(图论)