排列

来源:互联网 发布:医用冷藏箱知乎 编辑:程序博客网 时间:2024/04/28 18:53

题目描述

一个关于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。
N<=10000

DP

首先,几个显然的结论
1、答案所代表的意义为把N分解成若干份能得到这些份的乘积最大值。
2、一些大于1的数的乘积大于它们的和
3、任意两份互质
4、得到方案后,输出排列的最优方案:把份从小到大排序,然后把N个数按照排列后分割,每份转一下。例如
n=14,最优是拆成3+4+7,排序后就是3、4、7
那么第一份割三个即1、2、3,转一下变成2、3、1
第二份割四个即4、5、6、7,转一下变成5、6、7、4
剩下同理。
我们设f[i,j]表示当前做到第i个质数,现在和为j,能得到的最大积。
转移显然,见代码。
由于f数组存的值可能很大,而最后我们并不需要输出答案(只需要输出最优排列),所以可以用自然对数优化:
注意到a*b=c,对应ln(a)+ln(b)=ln(c)
用pre[i,j]表示状态f[i,j]的最优解从哪个状态转移过来。

参考程序

#include<cstdio>#include<algorithm>#include<cmath>#include<iostream>#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;typedef double db;db f[1300+10][10000+10],ans,wdc;int pre[1300+10][10000+10],pri[1300+10],a[10000+10];bool bz[10000+10];int i,j,k,l,t,n,m,ca,top,tot;int main(){    top=0;    fo(i,2,10000){        if (!bz[i]) pri[++top]=i;        fo(j,2,10000/i) bz[i*j]=1;    }    fo(i,0,top)        fo(j,0,10000)            pre[i][j]=f[i][j]=0;    fo(i,1,top){        fo(j,0,10000){            f[i][j]=f[i-1][j];            pre[i][j]=j;            t=pri[i];            while (t<=j){                wdc=f[i-1][j-t]+log(t);                if (wdc>f[i][j]){                    f[i][j]=wdc;                    pre[i][j]=j-t;                }                t*=pri[i];            }        }    }    scanf("%d",&ca);    while (ca--){        scanf("%d",&n);        ans=0;        fo(i,0,n)             if (f[top][i]>ans) ans=f[top][i],k=i;        tot=0;i=1;        while (n-->k) printf("%d ",i++);        fd(j,top,1){            t=k-pre[j][k];            if (t) a[++tot]=t;            k=pre[j][k];        }        sort(a+1,a+tot+1);        fo(j,1,tot){            fo(k,i+1,i+a[j]-1) printf("%d ",k);            printf("%d ",i);            i+=a[j];        }        printf("\n");    }}
0 0
原创粉丝点击