POJ3590 The shuffle Problem [置换+dp]
给出正整数, , 输出长度为 的置换的 最长周期, 并请输出满足条件的 字典序最小 的置换 .
整个序列的循环节周期长度为所有 子循环节 周期长度 的最小公倍数 .
思维卡点: 每个子循环节的周期长度怎么求呢 ?
原来每个子循环节的周期长度是确定的 :
- 作为子循环节不能被划分成其他更小的子循环节
- 经过观察, 每个不可划分的子循环节周期长度都等于其本身长度,
所以得出结论: ,
到此, 问题就转化为: 将数划分为若干份, 使 尽量大.
设 表示 将 分为 份所得到的最大 ,
则
转移复杂度 .
首先最优值 已经被前面的 解决,
这个 为所有区间长度的 ,
则将其 , 得到 ,
每个带幂质数都代表着一个区间, 长度分别为 ,.
可能还会剩余一些 , 虽说对 没有贡献, 但还是要输出的, 输出 个 在前面
尽量将小的放到前面, 可以 , 拿纸画一下就出来了.
#include<cstdio>
#include<algorithm>
#define reg register
const int maxn = 108;
int T;
int N;
int cnt[maxn];
int F[maxn][maxn];
int Pre[maxn][maxn];
int p[] = {0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107};
int Gcd(int a, int b){ return !b?a:Gcd(b, a%b); }
int Lcm(int a, int b){ return a/Gcd(a, b) * b; }
void Work(){
scanf("%d", &N);
printf("%d ", F[N][0]);
int tmp = F[N][0], Ps = 0;
for(reg int i = 1; i <= 26; i ++){
cnt[i] = 1;
while(tmp % p[i] == 0) cnt[i] *= p[i], tmp /= p[i];
if(cnt[i] == 1) cnt[i] = 0;
Ps += cnt[i];
}
int t = N - Ps;
for(reg int i = 1; i <= t; i ++) printf("%d ", i);
t ++;
std::sort(cnt+1, cnt+26+1);
for(reg int i = 1; i <= 26; i ++){
if(!cnt[i]) continue ;
int tmp = t;
for(reg int j = 1; j < cnt[i]; j ++) printf("%d ", t+j);
printf("%d ", tmp);
t += cnt[i];
}
printf("\n");
}
int main(){
for(reg int i = 1; i <= 105; i ++){
F[i][1] = i;
for(reg int j = 2; j <= i; j ++)
for(reg int k = 1; k <= i; k ++)
F[i][j] = std::max(Lcm(F[i-k][j-1], k), F[i][j]);
}
for(reg int i = 1; i <= 105; i ++)
for(reg int j = 1; j <= i; j ++) F[i][0] = std::max(F[i][0], F[i][j]);
scanf("%d", &T);
while(T --) Work();
return 0;
}