hdu 2894 DeBruijin

呵呵,好神奇,题目隐含的意思就是要找出一条欧拉回路

先看下这个详细 的解释,欧拉图的应用:

计算机鼓轮的设计。设有旋转鼓轮其表面被等分成24个部分,如图7-4.4所示。

其中每一部分分别用绝缘体或导体组成,绝缘体部分给出信号0,导体部分给出信号1,在图7-4.4中阴影部分表示导体,空白部分表示绝缘体,根据鼓轮的位置,触点将得到信息1101,如果鼓轮沿顺时针方向旋转一个部分,触点将有信息1010。问鼓轮上16个部分怎样安排导体及绝缘体,才能使鼓轮每旋转一个部分,四个触点能得到一组不同的四位二进制数信息。

设有一个八个结点的有向图(图7-4.5),其结点分别记为三位二进制数{000,001,010,011,100,101,110,111},设ai∈{0,1>,从结点a1a2a3可引出两条有向边,其终点分别是a2a30以及a2a3l。该两条边分别记为a1a2a30和a1a2a3l。按照上述方法,对于八个结点的有向图共有16条边,在这种图的任一条路中,其邻接的边必是a1a2a3a4和a2a3a4a5的形式,即是第一条边标号的后三位数与第二条边标号的头三位数相同。因为图中16条边被记成不同的二进制数,可见前述鼓轮转动所得到16个不同位置触点上的二进制信息,即对应于图中的一条欧拉回路。在图7-4.5中,每个结点的入度等于2,出度等于2,故在图中必可找到一条欧拉回路如(e0e1e2e4e9e3e6e13e10e5e11e7e15e14e12e8),根据邻接边的标号记法,这16个二进制数可写成对应的二进制数序列0000100110101111。把这个序列排成环状,即与所求的鼓轮相对应,如图7-4.4所示。
上面的例子,我们可以把它推广到鼓轮具有n个触点的情况。为此我们只要构造2n-1个结点的有向图,设每个结点标记为n-1位二进制数,从结点a1a2…an-1出发,有一条终点为a2a3…an-10的边,该边记为a1a2…an-10;还有一条边的终点为a2a3…an-11的边,该边记为a1a2…an-11。这样构造的有向图,其每一结点的出度和入度都是2,故必是欧拉图。由于邻接边的标记是第一条边的后n-1位二进制数与第二条边的前n-1位二进制数相同,为此就有一种2n个二进制数的环形排列与所求的鼓轮相对应。

有了上面的理论基础之后,就是一个寻找欧拉回路的问题了,本身是一定存在的,所以DFS就可以了,涉及到了位运算,由上面可以知道,构造欧拉路是根据前k-1个位寻找下一个节点,用这一步运算就可以找到下一个节点了 t=((u<<1)&((1<<n)-1)), 自己模拟一下就知道了

另外,要按照字典序找出最小的,只需要每次DFS时,先添加0,再添加1就可以了;

最后就是一个逆序输出的问题了

View Code
#include<iostream>
#include
<string>
using namespace std;
int flag[(1<<11)+1],n,cnt,ans[(1<<11)+1];
void dfs(int u)
{
int t=((u<<1)&((1<<n)-1));
if(!flag[t])
{
flag[t]
=1;
dfs(t);
ans[cnt
++]=0;
}
if(!flag[t+1])
{
flag[t
+1]=1;
dfs(t
+1);
ans[cnt
++]=1;
}
}
int main()
{
while(cin>>n)
{
memset(flag,
0,sizeof(flag));
cnt
=0;
dfs(
0);
cout
<<(1<<n)<<' ';
for(int i=1;i<n;i++)
putchar(
'0');
for(int i=cnt-1;i>=n-1;i--)
cout
<<ans[i];
putchar(
'\n');
}
return 0;
}
posted @ 2011-08-06 17:48  枕边梦  阅读(959)  评论(0编辑  收藏  举报