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 }