poj1780 code 欧拉回路

题目链接:

poj1780

题意:

KEY 公司开发出一种新的保险箱。要打开保险箱,不需要钥匙,但需要输入一个正确的、由n 位数字组成的编码。这种保险箱有几种类型,从给小孩子玩的玩具(2 位数字编码)到军用型的保险箱(6 位数字编码)。当正确地输入最后一位编码后,保险箱就立刻打开了。保险箱上没有“确定”键。当你输入超过n 位数字,则只有最后n 位数字有效。例如,对一种4 位数字编码的型号,如果正确的编码为4567,你想输入的编码为1234567890,则保险箱的门会在你输入数字7
后马上就打开了。为了达到这种效果所需要设计的软件其实很简单。对n 位数字编码的型号,保险箱始终处于10(n-1)种内部状态之一。保险箱的当前状态只需用最后输入的n-1 位数字表示,其中有一种状态(例如,对前面的例子,就是456)被记为“开锁状态”。如果保险箱处于“开锁状态”,且输入最后一位正确的数字(例如,在上面的例子中就是7),保险箱的门就打开了;否则保险箱切换到对应的新状态。例如,如果保险箱的当前状态为456,接着输入8,则保险箱的状态切换到568。为了开保险箱,一个繁琐的策略是一位接一位地输入所有可能的编码。然而,在最坏情况下,这需要按键n×10n
次(有10n 组可能的编码,每个编码有n 位)。而选择一个好的数字序列,最多只需要按键10n + n - 1 次就可以打开保险箱了:你需要做的就是找到一个数字序列包含所有的n 位数一次且仅一次。KEY 公司宣称,对军用型号(n = 6),当今最快的计算机也需要数十亿年的时间才能找到这样的数字序列,但是很显然他们不知道有些程序员能在几分钟就能找到这样的数字序列。

题解思路:

弗洛莱算法求解欧拉回路,将(n-1)位以内的所有数当做图中的点,将0~9当做图中每个点的边,再根据dfs按顺序递归求解,即可得答案,但直接递归会爆栈我们需要用到一个中间栈来模拟递归的过程,最后得到相应的答案.

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define maxn 1000500
using namespace std;
int ans[maxn],ss;
int Stack[maxn];
int cnt[maxn/10];
int top;
void non_dfs(int x,int tail)
{
    while(cnt[x]<10)
    {
        int value=x*10+cnt[x];
        cnt[x]++;
        Stack[top++]=value;
        x=value%tail;
    }
}
int main()
{
    int d,n,tail;
    while(scanf("%d",&n)&&n)
    {
        ss=0;
        top=0;
        tail=1;           //每个数对tail 取模即可得到下一个点的前缀
         for(int i=0;i<n-1;i++)
            tail*=10;
        for(int i=0;i<=tail;i++)
            cnt[i]=0;

        non_dfs(0,tail);
        while(top)
        {
            d=Stack[--top];
            ans[ss++]=d%10;      //只需取最后一位即可
            non_dfs(d/10,tail); //往前一个数继续搜
        }
        for(int i=0;i<n-1;i++)
            printf("0");
        for(int i=ss-1;i>=0;i--)
            printf("%d",ans[i]);
        printf("\n");
    }
    return 0;
}
时间: 06-23