POJ 3590 The shuffle Problem

Description

Any case of shuffling of n cards can be described with a permutation of 1 to n. Thus there are totally n! cases of shuffling. Now suppose there are 5 cards, and a case of shuffle is <5, 3, 2, 1, 4>, then the shuffle will be:

Before shuffling:1, 2, 3, 4, 5
The 1st shuffle:5, 3, 2, 1, 4
The 2nd shuffle:4, 2, 3, 5, 1
The 3rd shuffle:1, 3, 2, 4, 5
The 4th shuffle:5, 2, 3, 1, 4
The 5th shuffle:4, 3, 2, 5, 1
The 6th shuffle:1, 2, 3, 4, 5(the same as it is in the beginning)

You’ll find that after six shuffles, the cards’ order returns the beginning. In fact, there is always a number m for any case of shuffling that the cards’ order returns the beginning after m shuffles. Now your task is to find the shuffle with the largest m. If there is not only one, sort out the one with the smallest order.

Input

The first line of the input is an integer T which indicates the number of test cases. Each test case occupies a line, contains an integer n (1 ≤ n ≤ 100).

Output

Each test case takes a line, with an integer m in the head, following the case of shuffling.

Sample Input

2
1
5
Sample Output

1 1
6 2 1 4 5 3

题意

  求解让一个置换变为单位置换的最大步数,并把置换按字典序输出,也就是Tk = e,有多个T,但是T的循环节长度之和为n,求解k的最大值,并且T是字典序最小的那一个。

思路

  把n分解成若干个数,使得他们的lcm最大。很明显在所取的数都是素数幂的时候是最大的情况,并且最好每个循环节的长度不同,所以可以用递归来枚举所有的分解情况,而且由于要输出序最小的,所以对于剩下的数可以直接单独都作为一个循环,这样就可以使得序最小了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<ctype.h>
#include<vector>
#include<algorithm>
#include<sstream>
#define PI acos(-1.0)
using namespace std;
typedef long long ll;
typedef unsigned long long llu;
const int inf = 0x3f3f3f3f;
const ll lnf = 0x3f3f3f3f3f3f3f3f;
const int maxn = 210;
bool ha[maxn];
int p[maxn],lp;
void prim()	//筛素数,n比较小,就应最简单的筛法了
{
    memset(ha,true,sizeof(ha));
    lp=0;
    for(int i=2;i<maxn;i++){
        if(ha[i]){
            p[lp++]=i;
            for(int j=i*i;j<maxn;j+=i)
                ha[j]=false;
        }
    }
}
int st[maxn],maxm,c[maxn],lc;	
void dfs(int remain,int k)
{
    if(remain<p[k]){
        int m = 1;
        for(int i=0;i<k;i++)	//求lcm
            if(st[i])
                m *= st[i];
        if(m>maxm){
            maxm = m;   //更新最大k值
            lc = 0;	//更新循环
            for(int i=0;i<k;i++)	
                if(st[i])
                    c[lc++] = st[i];
            while(remain--)	//剩余的补1
                c[lc++] = 1;
        }
    }
    else{
        st[k] = 0;
        dfs(remain,k+1);
        for(st[k]=p[k];st[k]<=remain;st[k]*=p[k])
            dfs(remain-st[k],k+1);
    }
}
int main(void)
{
    int t,n;
    prim();
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        if(n==1){	//特判
            printf("1 1\n");
            continue;
        }
        //找寻最大lcm,
        maxm=1;lc=0;	//最大k值,循环节个数
        dfs(n,0);
        sort(c,c+lc);	//按循环节长度,由小到大排
        
        printf("%d",maxm);
        int k=1,tmp;
        for(int i=0;i<lc;i++){
            tmp = k++;
            for(int j=1;j<c[i];j++)
                printf(" %d",k++);
            printf(" %d",tmp);
        }
        printf("\n");
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逃夭丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值