POJ1780 Code(欧拉路径)

n位密码,要用尽可能短的序列将n位密码的10n种状态的子串都包括,那么要尽量地重合。

题目已经说最短的是10n + n - 1,即每一个状态的后n-1位都和序列中后一个状态的前n-1位重合。

这题是经典的欧拉路径问题吧,用n位数字10n种状态来作为边,而用重合的n-1位数字表示点。

具体的建图,每个点都引出10条边(十进制),这10条边就代表着10个n位数,前n-1位的数就代表那个点,然后连向这个边代表数的后n-1位代表的点。。

比如n等于3的时候这么建图(假设密码是二进制,十进制太多了):

这样子如果图存在欧拉路径,那么就能构成最短的10n + n - 1序列包含所有n位10进制密码的子串,而这个序列就通过每条边表示数字的最后一位来构造。

 

而这题听说不用真正地建图,DFS就行了。

DFS要写成非递归的,POJ好像不能手动扩栈。

改写非递归也不难:每个结点会被访问10次,每一次从0到9扩展结点入栈;访问第11次时出栈并还原,相当于递归的回溯部分了。

另外这题要字典序最小。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<queue>
 4 using namespace std;
 5 
 6 int n,pow[]={1,10,100,1000,10000,100000,1000000};
 7 int cnt[1000000],stack[1000010];
 8 bool vis[1000000];
 9 void dfs(){
10     memset(vis,0,sizeof(vis));
11     vis[0]=1;
12     memset(cnt,0,sizeof(cnt));
13     int top=0;
14     stack[++top]=0;
15     while(top!=pow[n]){
16         int u=stack[top];
17         if(cnt[u]==10){
18             vis[u]=0; cnt[u]=0;
19             --top;
20             continue;
21         }
22         ++cnt[u];
23         int v=(u*10+cnt[u]-1)%pow[n];
24         if(vis[v]) continue;
25         vis[v]=1;
26         stack[++top]=v;
27     }
28     for(int i=1; i<=top; ++i) putchar(stack[i]%10+'0');
29 }
30 int main(){
31     while(~scanf("%d",&n) && n){
32         for(int i=1; i<n; ++i) putchar('0');
33         dfs();
34         putchar('\n');
35     }
36     return 0;
37 } 

 

posted @ 2016-01-08 19:54  WABoss  阅读(343)  评论(0编辑  收藏  举报