zoukankan      html  css  js  c++  java
  • 排列

    题目

    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。

    解题思路

    我们再观察这时候的答案,显然,对于一个排列,最大的秩就是所有环大小的最小公倍数。因此,问题转化成:
    把n拆成几个正整数的和(正整数可以为1),这些正整数的最小公倍数就是我们要的最大的秩,而字典序最小的拆法就是我们要输出的东西。

    最小公倍数归根到底是很多质数的乘积,因此我们直接用质数来构造。

    现在问题变成:
    现在有一堆质数((n)以内),每个质数的使用次数上限是已知的(就是上面的(c)的大小上限)。我要选择一些质数(或它的幂),使他们的和(leq n),然后这些质数(或它的幂)的乘积要最大。
    而这其实就是有限背包问题。

    (f_{i,j})表示我们处理到第(i)个质数、当前和为(j)所能获得的最大秩。则 (f_{i,j} = max(f_{i-1,j-p_i^k}*p_i^k)) 。而我们要求排列的话,只需记录一下每个状态是由哪个状态转移过来的,最后还原即可。

    但是

    (f)的值很大很大,大过(long long),所以我们要把f的值转为自然对数来做。由于自然对数是单调函数,所以比较大小的方式一毛一样。

    (Code)

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    int a[100005],vis[10005],n,g[1230][10005],p[10005],tot = 0;
    double f[1230][10005];
    
    void init()
    {
    	vis[1] = 1;
    	for (int i = 2; i <= 10005; i++)
    	{
    		if (!vis[i]) p[++tot] = i;
    		for (int j = 1; j <= tot && p[j] * i <= 10005; j++)
    		{
    			vis[p[j] * i] = 1;
    			if (i % p[j] == 0) break;
    		}
    	}
    }
    int main()
    {
    	int t;
    	scanf("%d",&t);
    	init();
    	while (t--)
    	{
    		scanf("%d",&n);
    		memset(f,0,sizeof(f));
    		f[0][0] = (double)log(1.0);
    		int g2;
    		for (int i = 1; i <= tot; i++)
    		{
    			for (int j = n; j >= 1; j--)
    			{
    				f[i][j] = f[i - 1][j];
    				int k = 1;
    				while (k * p[i] <= j)
    				{
    					k = k * p[i];
    					if (f[i - 1][j - k] + (double)log(k * 1.0) > f[i][j]) 
    						f[i][j] = f[i - 1][j - k] + (double)log(k * 1.0),
    							g[i][j] = k;
    				}
    			}
    		}
    		if (n == 1) 
    		{
    			printf("1
    ");
    			continue;
    		}
    		int g1;
    		double ans = 0;
    		for (int i = 1; i <= n; i++)
    			if (f[tot][i] > ans) ans = f[tot][i],g1 = i;
    		int l = g1,j = 0;
    		for (int i = tot; i; i--)
    		{
    			if (g[i][l]) a[++j] = g[i][l];
    			l -= g[i][l];
    		}
    		sort(a + 1,a + 1 + j);
    		int sum = n - g1 + 1;
    		for (int i = 1; i < sum; i++) 
    			printf("%d ",i);
    		for (int i = 1; i <= j; i++)
    		{
    			for (int k = sum + 1; k <= sum + a[i] - 1; k++)
    				printf("%d ",k);
    			printf("%d ",sum);
    			sum += a[i];
    		}
    		printf("
    ");
    	}
    }
    
  • 相关阅读:
    web前端开发最佳实践--(笔记之JavaScript最佳实践)
    web前端开发最佳实践--(笔记一)
    HTML5及CSS3--freeCodeCamp(笔记一)
    javascript系列--函数(一)
    HTML5本地存储
    分享一些好用的设计网站
    .net面试问题总结
    ife_task10--弹性盒子
    WPF--搭建一个简单的demo
    信息技术文集
  • 原文地址:https://www.cnblogs.com/nibabadeboke/p/13455387.html
Copyright © 2011-2022 走看看