• [背包]JZOJ 3232 【佛山市选2013】排列


    Description

    一个关于n个元素的排列是指一个从{1, 2, …, n}到{1, 2, …, n}的一一映射的函数。这个排列p的秩是指最小的k,使得对于所有的i = 1, 2, …, n,都有p(p(…p(i)…)) = i(其中,p一共出现了k次)。

    例如,对于一个三个元素的排列p(1) = 3, p(2) = 2, p(3) = 1,它的秩是2,因为p(p(1)) = 1, p(p(2)) = 2, p(p(3)) = 3。

    给定一个n,我们希望从n!个排列中,找出一个拥有最大秩的排列。例如,对于n=5,它能达到最大秩为6,这个排列是p(1) = 4, p(2) = 5, p(3) = 2, p(4) = 1, p(5) = 3。

    当我们有多个排列能得到这个最大的秩的时候,我们希望你求出字典序最小的那个排列。对于n个元素的排列,排列p的字典序比排列r小的意思是:存在一个整数i,使得对于所有j < i,都有p(j) = r(j),同时p(i) < r(i)。对于5来说,秩最大而且字典序最小的排列为:p(1) = 2, p(2) = 1, p(3) = 4, p(4) = 5, p(5) = 3。
     

    Input

    输入的第一行是一个整数T(T <= 10),代表数据的个数。

    每个数据只有一行,为一个整数N。

    Output

    对于每个N,输出秩最大且字典序最小的那个排列。即输出p(1), p(2),…,p(n)的值,用空格分隔。
     

    Sample Input

    2
    5
    14

    Sample Output

    2 1 4 5 3
    2 3 1 5 6 7 4 9 10 11 12 13 14 8
     

    Data Constraint

    对于40%的数据,有1≤N≤100。

    对于所有的数据,有1≤N≤10000。

    分析

    稍作分析就可以发现这是要把一个正整数分为多个正整数,其最小公倍数最大的正整数组的最小字典序

    考虑到排列很难构造,那我们直接构造最小公倍数

    它必定是若干个质数的乘积

    那么题中的最小公倍数则可以表示为

    W=p1^c1*p2^c2*p3^c3…… p是质数 c是质数的指数,p之和不超过n

    如果比n小怎么办?直接在排列最前端补足1 2 3 4 5 6……

    那么可得f[i][j]为处理了第i个质数,和为j的最大最小公倍数(因为是质数直接乘)

    f[i][j]=f[i-1][j-w]*w w为质数或质数的幂

    至于答案,只需要用g[i][j]记录一下状态是从哪个状态转移过来的即可

    注意,f的值会非常大,可以用自然对数法,也可以直接用double(本题几乎无视精度问题)

    #include <iostream> 
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    typedef double ld;
    const int N=1e4+10;
    int prime[1310],cnt;
    bool b[N];
    int t,n;
    int from[1310][N],cunt,a[1310],acnt;
    ld f[1310][N],mx; 
    
    void Get_Prime() {
        for (int i=2;i<=10000;i++) {
            if (!b[i]) prime[++cnt]=i;
            int j=2*i;
            while (j<=10000) {
                b[j]=1;
                j+=i;
            }
        }
    }
    
    void Get(int last) {
        int dep=cnt;
        while (dep) {
            if (last-from[dep][last]) a[++acnt]=last-from[dep][last];
            last=from[dep][last];dep--;
        }
    }
    
    int main() {
        Get_Prime();
        for (scanf("%d",&t);t;t--) {
            scanf("%d",&n);
            if (n<5) {
                for (int i=2;i<=n;i++) printf("%d ",i);
                printf("1
    ");
                continue;
            }
            mx=0;
            for (int i=1;i<=cnt;i++)
                for (int j=0;j<=n;j++) f[i][j]=0;
            f[0][0]=1;
            for (int i=1;i<=cnt;i++) {
                int l=prime[i];
                for (int j=0;j<=n;j++) f[i][j]=f[i-1][j],from[i][j]=j;
                for (;l<=n;l*=prime[i]) {
                    for (int j=l;j<=n;j++)
                        if (f[i][j]<f[i-1][j-l]*1.0*l) {
                            f[i][j]=f[i-1][j-l]*1.0*l;
                            from[i][j]=j-l;
                        }
                }
            }
            ld mx=0;int last;
            for (int i=1;i<=n;i++)
                if (mx<f[cnt][i]) {
                    mx=f[cnt][i];
                    last=i;
                }
            for (int i=1;i<=n-last;i++) printf("%d ",i);
            cunt=n-last;acnt=0;
            Get(last);
            sort(a+1,a+acnt+1);
            for (int i=1;i<=acnt;i++) {
                for (int j=2;j<=a[i];j++) printf("%d ",j+cunt);
                printf("%d ",1+cunt);
                cunt+=a[i];
            }
            printf("
    ");
        }
    }
    View Code
    在日渐沉没的世界里,我发现了你。
  • 相关阅读:
    http请求的跨域
    事件驱动模型
    单例设计
    引用类型与垃圾回收
    注解开发
    Java的反射机制
    Android Listener 监听的几种写法
    人生必须有所规划,工作是好比马拉松
    Android 常用的常量
    Android 常见的广播 action常量
  • 原文地址:https://www.cnblogs.com/mastervan/p/11107804.html
Copyright © 2020-2023  润新知