POJ-1780 Code

34 篇文章 0 订阅
10 篇文章 0 订阅
Code
Time Limit: 1000MS Memory Limit: 65536K
   

Description

KEY Inc., the leading company in security hardware, has developed a new kind of safe. To unlock it, you don't need a key but you are required to enter the correct n-digit code on a keypad (as if this were something new!). There are several models available, from toy safes for children (with a 2-digit code) to the military version (with a 6-digit code). 

The safe will open as soon as the last digit of the correct code is entered. There is no "enter" key. When you enter more than n digits, only the last n digits are significant. For example (in the 4-digit version), if the correct code is 4567, and you plan to enter the digit sequence 1234567890, the door will open as soon as you press the 7 key. 

The software to create this effect is rather simple. In the n-digit version the safe is always in one of 10 n-1 internal states. The current state of the safe simply represents the last n-1 digits that have been entered. One of these states (in the example above, state 456) is marked as the unlocked state. If the safe is in the unlocked state and then the right key (in the example above, 7) is pressed, the door opens. Otherwise the safe shifts to the corresponding new state. For example, if the safe is in state 456 and then you press 8, the safe goes into state 568. 

A trivial strategy to open the safe is to enter all possible codes one after the other. In the worst case, however, this will require n * 10 n keystrokes. By choosing a good digit sequence it is possible to open the safe in at most 10 n + n - 1 keystrokes. All you have to do is to find a digit sequence that contains all n-digit sequences exactly once. KEY Inc. claims that for the military version (n=6) the fastest computers available today would need billions of years to find such a sequence - but apparently they don't know what some programmers are capable of...

Input

The input contains several test cases. Every test case is specified by an integer n. You may assume that 1<=n<=6. The last test case is followed by a zero.

Output

For each test case specified by n output a line containing a sequence of 10 n + n - 1 digits that contains each n-digit sequence exactly once.

Sample Input

1
2
0

Sample Output

0123456789
00102030405060708091121314151617181922324252627282933435363738394454647484955657585966768697787988990
————————————————————沉默的分割线————————————————————
前言:此题不是很简单,主要是建图的思想以及dfs改写成显示栈。
思路:对于一个包含了n位数所有可能的排列的一串数字,相当于从一个n位数字串开始,每增加一个数字,就构成了一个废弃数字+新的n位数字。那么相互重叠的部分就是点,废弃的数字和新增加的数字就是边。
那就是一个欧拉回路的问题——经过每个边一次且仅一次。
暴力枚举这n位数字,然后将每个数分割为两个部分,前n-1位和后n-1位。
那么从前n-1位增加一个数,到达后n-1位,因为枚举的数字是独一无二的,故(前n-1位, 后n-1位)这个数对也是独一无二的,该边即是独一无二的。
之后跑一次深搜。取某点的头指针,遍历它的边,直到出现某点无边可走,说明刚走过桥了,将桥压栈。
P.S. 注意一开始一定是一个n位的0。
代码如下:
/*
ID: j.sure.1
PROG:
LANG: C++
*/
/****************************************/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <string>
#include <climits>
#include <iostream>
#define INF 0x3f3f3f3f
using namespace std;
/****************************************/
const int M = 1e6+10;
int n, head[M], tot, pi;
char path[M];
struct Node {
	int v, w, next;
}edge[M];
struct STA {
	int w, head;
}sta[M];

void add(int u, int v, int w)
{
	edge[tot].v = v;
	edge[tot].w = w;
	edge[tot].next = head[u];
	head[u] = tot++;
}
/*
void dfs(int u, int w)
{
	int i;
	for(i = head[u]; i != -1; i = edge[i].next) {
		if(edge[i].v != -1) {
			 int v = edge[i].v;
			 edge[i].v = -1;
			 dfs(v, edge[i].w);
		}
	}
	if(i == -1) {
		if(w >= 0) path[pi++] = w + '0';
		return ;
	}//递归边界,无边可走
}*///恰好在n=6的时候爆栈

void Euler()
{
	int top = -1;
	pi = 0;
	sta[0].w = -1; sta[0].head = head[0];//0前面没有数字,假设是-1和0之间的边,权值-1
	top++;
	while(top != -1) {
		STA &nt = sta[top];//取栈首
		int i;
		for(i = nt.head; i != -1; i = edge[i].next) {
			if(edge[i].v != -1) {//没有拜访过
				int v = edge[i].v;
				edge[i].v = -1;//已经拜访的边,相当于vis标记
				top++;
				sta[top].w = edge[i].w;
				sta[top].head = head[v];
				break;
			}
		}
		if(i == -1) {
			if(nt.w >= 0) path[pi++] = nt.w + '0';
			top--;
		}
	}
}

int main()
{
#ifdef J_Sure
//	freopen("000.in", "r", stdin);
//	freopen(".out", "w", stdout);
#endif
	while(scanf("%d", &n), n) {
		if(n == 1) puts("0123456789");
		else {
			int m = 1;
			tot = 0;
			for(int i = 1; i < n; i++) m *= 10;//m = 10^(n-1)
			memset(head, -1, sizeof(head));
			for(int i = m*10; i >= 0; i--) {
				int u = i / 10, v = i % m, w = i % 10;
				//1234 -> 123,234,4
				add(u, v, w);
			}//暴力建图
	//		dfs(0, -1);
			Euler();
			for(int i = 1; i < n; i++)
				path[pi++] = '0';//一开始应该是n-1个0,而不是单纯的0
			for(int i = pi-1; i >= 0; i--) {
				putchar(path[i]);
			}
			puts("");
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值