acm.hdu.edu.cn/showproblem.php?pid=2894
磁鼓模型欧拉回路
解题思路(以样例为例):第一问很简单,就是2^k个扇形。下面分析第二问,当前指向001的话,磁鼓转动一个扇形,下一个指向会是010或011。像01这样的后缀,有2^(k-1)个,且[0,2^(k-1))区间包含了这2^(k-1)个不同的后缀。如果现在的数是n,那么我们从n向(n<<1),(n<<1|1)连一条边,由于要求字典序,n到(n<<1)这条边要在另一条之前。这样建立图之后,由于一定含有欧拉路径,只需记录下来即可。这里有一个问题,我们需要在搜索之后记录路径,因为从一个节点出发,随便往下走,必然也在这个节点终止(因为除了起始节点,其他节点的度都是偶数,只要能进去就能出来),走完一个圈后,因为是随便走的,所以可能会有些边还没走过就回来了。我们就从终止节点逆着往前查找,在成环的地方,破环成链,最终找到欧拉回路。
#include <math.h>
#include <vector>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
int k;
int head[1<<17],cnt;//head[u]记录的是以u为起点插入的第一条边的序号
int vis[1<<17],path[1<<17],top;
struct edge{
int v,id,next;
}node[1<<17];
void init(){
memset(head, -1, sizeof(head));
memset(vis, 0, sizeof(vis));
cnt = 0, top = 0;
}
void addedge(int u, int v, int id){
node[cnt].v = v;
node[cnt].id = id;
node[cnt].next = head[u];
head[u] = cnt++;
}
void DFS(int u){
for(int i=head[u]; i!=-1; i=node[i].next){
if(!vis[node[i].id]){
vis[node[i].id] = 1;
DFS(node[i].v); //先搜索
path[top++] = node[i].id;//后记录
}
}
}
int main(){
while(scanf("%d",&k) == 1){
init();
int ans = 1 << k;
int len = 1 << (k - 1);//后缀的数量
for(int i = 0; i < len; i++){
int id = i << 1 | 1, v = id % len;//id和v的后缀是一样的,由于i只到len-1,因此必须取模处理
addedge(i, v, id);
id = i << 1, v = id % len;
addedge(i, v, id);
}
//for(int i=head[0]; i!=-1; i=node[i].next)
// cout << i << " " << node[i].id << " " << node[i].v << endl;
DFS(0);
cout << ans << " ";
for(int i=top-1; i>=0; i--)
cout << path[i]/len;
cout << endl;
}
return 0;
}