忙了好久,终于搞定了,每次将不会的题琢磨完了,都感觉是一次小小的进步,庆祝一下下。。呵呵
首先是建图,题意要求所有的n位数编码成一个数字串,每个n位数只出现一次,可以把n位数看成边,那么可以把它的前n-1位看成点A,后n-1一位看成点B,那么它就是A->B的一条边了,这样我们就可以对10^(n - 1)个点建图,每个节点对另外10个点有边,找一次欧拉回路就行了。
这题有一难点,要求按最小字典序输出,我用邻接表存的边,然后对于每个点的链表是按字典序建的边,这样再用圈套圈算法,然后逆序输出,就可以求的最小序结果了。
还有个一个问题,如果递归写的话,就会系统栈爆栈,导致RE,所以需要我们改成非递归的形式,这点让我郁闷了好久。。 不过还是让我给改出来了,收获!让我对系统栈又有了更深的理解。
//============================================================================ // Description : 圈套圈算法,逆序输出欧拉回路 //============================================================================ #include <iostream> #include<stdio.h> #include<string.h> #define NN 1000020 using namespace std; typedef struct node{ //int u; int v; int vis; struct node *nxt; }NODE; NODE *Link[NN / 10]; NODE edg[NN]; NODE *sta[NN]; // 模拟系统栈 int idx, kind, n, top; int stk[NN]; void Init(){ memset(Link, 0, sizeof(Link)); idx = top = 0; } void Add(int u, int v){ //edg[idx].u = u; edg[idx].v = v; edg[idx].vis = 0; edg[idx].nxt = Link[u]; Link[u] = edg + idx++; } void dfs(int u){ // 递归形式 for (NODE *p = Link[u]; p; p = p->nxt){ if(!p->vis){ p->vis = 1; Link[u] = p->nxt; dfs(p->v); stk[++top] = p->v % 10; } } } void dfs1(int u){ // 非递归形式 int top0 = 0; NODE *p; sta[++top0] = Link[u]; while(top0){ for (p = sta[top0]; p; p = p->nxt){ if(!p->vis){ p->vis = 1; sta[top0] = p; // 递归进入下一层的时候,先保存入口 sta[++top0] = Link[p->v]; //进入下一层 Link[u] = p->nxt; // 更改链表头指针 u = p->v; // 这点很重要,每次进入下一次递归的时候需要更改,否则不变 break; } } if(p == NULL){ top0--; if(top0 == 0) break; stk[++top] = sta[top0]->v % 10; sta[top0] = sta[top0]->nxt; } } } void Solve(){ int i; dfs1(0); for (i = 1; i < n; i++){ printf("0"); } for (i = top; i >= 1; i--){ printf("%d", stk[i]); } puts(""); } int main() { int i, j; while(scanf("%d", &n) != EOF){ if(n == 0) break; if(n == 1){ puts("0123456789"); continue; } kind = 1; // kinds or number for (i = 1; i < n; i++){ kind = kind * 10; } Init(); int mod = kind / 10; for (i = 0; i < kind; i++){ int tmp = i % mod; tmp *= 10; for (j = 9; j >= 0 ; j--){ //if(tmp + j != i){ Add(i, tmp + j); //} } } Solve(); } return 0; }