zoukankan      html  css  js  c++  java
  • ACM 擅长排列的小明

    擅长排列的小明

    时间限制:1000 ms  |  内存限制:65535 KB
    难度:4
     
    描述
    小明十分聪明,而且十分擅长排列计算。比如给小明一个数字5,他能立刻给出1-5按字典序的全排列,如果你想为难他,在这5个数字中选出几个数字让他继续全排列,那么你就错了,他同样的很擅长。现在需要你写一个程序来验证擅长排列的小明到底对不对。
     
    输入
    第一行输入整数N(1<N<10)表示多少组测试数据,
    每组测试数据第一行两个整数 n m (1<n<9,0<m<=n)
    输出
    在1-n中选取m个字符进行全排列,按字典序全部输出,每种排列占一行,每组数据间不需分界。如样例
    样例输入
    2
    3 1
    4 2
    样例输出
    1
    2
    3
    12
    13
    14
    21
    23
    24
    31
    32
    34
    41
    42
    43

    看到此题首先想到的使用递归方法实现,可以画出递归树结构,难点在于,利用深度遍历的同时,还要考虑到下一次遍历的数值不能和原来的重复。变换成树结构的话,子节点的数值不能与父节点及其父节点(祖辈节点)的数值相同。我们需要有一个数组结构来保存其祖辈节点。我首先使用的方法是在每一个节点都构造一个二维数值,利用二维数值的逻辑与或来保存父辈节点。但是还要考虑输出情况,输出是要从上往下的。所以我构造了一个树结构来还原这个递归遍历过程。
    下面是我的代码:
    #include <stdio.h>
    #include <stdlib.h>
    
    struct Node
    {
        int data;
        int ids;// 保存祖辈节点
        Node* parent;// 父节点用于递归输出
    };
    
    void outPut(Node* node)
    {
        if(node->parent != NULL)
            outPut(node->parent);
        if(node->data != 0)
            printf("%d", node->data);
    }
    
    void loop(Node* node, int n, int m)
    {
        int count = 1, i = 1;
        if(m < 1)
        {
            outPut(node);
            printf("
    ");
            return;
        }
    
        for(;count <= n;count++)
        {    
            if((node->ids & (1 << count)) != 0)
            {
                Node* newNode = (Node*)malloc(sizeof(Node));
                newNode->ids = node->ids & (~(1 << count));
                newNode->parent = node;
                newNode->data = count;
                loop(newNode, n, m-1);
            }
        }
    }
    
    int main()
    {
        int N = 0;
        scanf("%d", &N);
        while(N--)
        {
            int n = 0, m = 0, count = 1, i = 0;
            scanf("%d%d", &n, &m);
            Node* node = (Node*)malloc(sizeof(Node));
            node->ids = 0xFFFFFFFF;
            node->data = 0;
            node->parent = NULL;
            loop(node, n, m);
        }
        return 1;
    }

    程序虽然速度还可以,但是占用空间较大。使用了树结构。

    然后我看了下最优代码,发现可优化的空间非常大。

    首先,保存祖辈节点的结构使用了两个数组来代替。一个used数组,用来记录使用过的数值(也就是祖辈节点),用过的键值对应的是1,没有是0;一个char数组,用来保存祖辈节点的顺序性(如 “12” “21”)。

    然后在递归遍历的同时,反复使用这两个数组记录祖辈节点,而不是每次递归到一个节点就记录一次。

    仔细想想,递归常常跟树结构相伴而生。树结构如果只需要一次遍历的话,只依靠递归就可以解决,但是递归不具有重现性,因为通过函数栈push到内存空间,然后pop后里面的所有参数和局部变量就不存在了。而我们自行构造的树结构可以保存在堆空间里,可以重现遍历操作。

    本题的最优解,就是最大程度的利用了递归方法,考略到需要适度的重现其祖父节点,就添加了两个全局数组变量。一定程度上弥补了递归的不可重现性。但是,也并没有实现完全可重现,全局变量需要高度重用。我实现的程序虽然占用了大量空间,但是却完整保留了递归的遍历过程。如果说给出任意一个节点,我就可以重现出关于它的遍历过程。

    就本题来讲,当然是追求最快的速度和最小的空间。完全不用完整保存遍历过程,所以见招拆招才是最有效的。

    最优解:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <algorithm>
    using namespace std;
    int n,m,used[10];
    char ans[11];
    void dfs(int x,int num)
    {
        if(num==m-1)
        {
            ans[m]='';
            printf("%s
    ",ans);
            return;
        }
        int i;
        for(i=1;i<=n;i++)
        {
            if(!used[i])
            {
                used[i]=1;
                ans[num+1]=i+'0';
                dfs(i,num+1);
                used[i]=0;
            }
        }
    }
    int main()
    {
        int i,t;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&m);
            for(i=1;i<=n;i++)
            {
                memset(used,0,sizeof(used));
                used[i]=1;
                ans[0]=i+'0';
                dfs(i,0);
            }
        }
        return 0;
    } 
  • 相关阅读:
    01pxc集群的部署
    Python简单爬虫获取岗位招聘人数
    nginx跨域问题记录
    自动备份远程mongodb数据库并拉取到本地
    ftp和mysql数据库结合使用
    flask蓝图的简单使用
    apache无法启动报错No space left on device
    简单的接口测试类和测试生成报告工具HTMLTestRunner.py
    简单python接口测试编写和django开发环境的搭建
    Fiddler模拟自动响应数据
  • 原文地址:https://www.cnblogs.com/sdlwlxf/p/4558603.html
Copyright © 2011-2022 走看看