poj 3590 The shuffle Problem (置换+分组背包)

The shuffle Problem
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 2115 Accepted: 702

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

Source

[Submit]   [Go Back]   [Status]   [Discuss]


题目大意:一个置换作多次后就可以回到最初的状态,假设这个次数成为循环节。那么这道题就是要求最大的循环节x,并且构造循环节为x的字典序最小的方案。

题解:置换+分组背包

这道题的做法与bzoj 1025是类似的。只不过我们还有开一个数组记录每一步的选择,在逆推回去。然后将轮换的长度从小到大排序,从1..n依次满足每个轮换。例如第一个轮换的长度是3,那么就输出(2,3,1)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 230
using namespace std;
int T,n,pd[N],prime[N],f[N][N],g[N][N],num[N],cnt;
void init(int n)
{
	for (int i=2;i<=n;i++) {
		if (!pd[i]) prime[++prime[0]]=i;
		for (int j=1;j<=prime[0];j++){
			if (prime[j]*i>n) break;
			pd[prime[j]*i]=1;
			if (i%prime[j]==0) break;
		}
	}
}
int main()
{
	freopen("a.in","r",stdin);
	scanf("%d",&T);
	init(100);
	while (T--){
		scanf("%d",&n);
		memset(f,0,sizeof(f));
		memset(g,0,sizeof(g));
		f[0][0]=1;
		for (int i=1;i<=prime[0];i++)
		 for (int j=n;j>=0;j--) {
		 	f[i][j]=f[i-1][j];
		 	g[i][j]=0;
		 	int now=prime[i];
		 	while (j>=now) {
		 		if (f[i][j]<f[i-1][j-now]*now)
				  f[i][j]=f[i-1][j-now]*now,g[i][j]=now;
		 		now*=prime[i];
			 }
		 }
		int ans=0,pos=0;
		int t=prime[0];
		for (int i=0;i<=n;i++)
		 if (f[t][i]>ans) {
		 	ans=f[t][i]; pos=i;
		 }
		printf("%d ",ans); int l=n-pos+1;
		for (int i=1;i<=n-pos;i++) printf("%d ",i);
		cnt=0; int j=pos;
		for (int i=prime[0];i>=1;i--) {
			if (g[i][j]) num[++cnt]=g[i][j];
			j-=g[i][j];
		}
		sort(num+1,num+cnt+1); 
		for (int i=1;i<=cnt;i++) {
			for (int j=l;j<=l+num[i]-2;j++) printf("%d ",j+1);
			printf("%d ",l);
			l+=num[i];
		}
		printf("\n");
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值