http://poj.org/problem?id=3590
题目的意思就是求最大的置换周期。很容易知道,整个序列的置换周期等于其中所有小循环的阶的最小公倍数,DP求之。
#include <cstdio>
#include <cstring>
const int maxn = 110;
int T,n;
int dp[maxn][maxn],ds[maxn][maxn],ans[maxn],anss[maxn],cnt[maxn];
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
int lcm(int a,int b)
{
if (a<b) {int tmp=a; a=b; b=tmp;}
return a*b/gcd(a,b);
}
int max(int a,int b)
{
return a>b?a:b;
}
void init()
{
memset(dp,0,sizeof(dp));
for (int i=1;i<=100;i++)
{
dp[i][1]=i;
ds[i][1]=i;
}
for (int j=2;j<=100;j++)
for (int i=j;i<=100;i++)
{
dp[i][j]=ds[i][j]=0;
for (int k=j-1;k<i;k++)
{
if (dp[i][j]<=lcm(dp[k][j-1],i-k))
{
dp[i][j]=lcm(dp[k][j-1],i-k);
ds[i][j]=i-k;
}
}
}
for (int i=1;i<=100;i++)
{
ans[i]=0;
for (int j=1;j<=i;j++)
{
if (ans[i]<=dp[i][j])
{
ans[i]=dp[i][j];
anss[i]=j;
}
}
}
}
int main()
{
init();
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
memset(cnt,0,sizeof(cnt));
int i=n,j=anss[n];
while (j>=1)
{
cnt[ds[i][j]]++;
i=i-ds[i][j];
j=j-1;
}
printf("%d",ans[n]);
for (i=1,j=1;i<=n&&j<=n;i++)
{
while (cnt[i])
{
for (int k=1;k<i;k++) printf(" %d",j+k);
printf(" %d",j);
j+=i;
cnt[i]--;
}
}
puts("");
}
return 0;
}