poj 3590 The shuffle Problem 置换群+DP
我们根据置换群的性质,知道其经过 K 次置换回到最初位置,
此时 K,为N元组的循环因子 a1,a2,...,ai 的最小公倍数。
现在题目要求输入N,求出最大的K,当K情况不唯一,则输出字典序最小,
那么题目就转换成将 N 分解成 X个数,其最小公倍数最大的情况了。
这里关于分解N的处理,也是参考一神牛,(再次ORZ一下)
因为 N <= 100 ,我们可以使用 DP 来得到。
F[ i ][ j ] = Max { F[ i ][ j ] , LCM( F[ i-k ][ j ], k ) }
定义 F[ i ][ j ] 表示 和为 i 分解为 j 个数时 的最小公倍数最大的值
通过保留最值及路径,来得出结果,ORZ神牛犀利的处理方案
具体看代码
解题代码
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<algorithm> using namespace std; const int N = 110; int f[N][N], d[N][N]; int val[N], size; int gcd(int a,int b) { return (b==0)? a : gcd(b,a%b); } int lcm(int a,int b) { return a/gcd(a,b)*b; } void init() { memset( f, 0, sizeof(f)); for(int i = 0; i < N; i++) { f[i][0] = 1; f[i][1] = i; } // f[i][j]: 和为i,分解成j个数的最小公倍数 最大值 for(int i = 2; i <= 100; i++) { for(int j = 2; j <= i; j++) { for(int k = 1; k <= i-j+1; k++) //分解为j个数时,k最大为 i-j+1 , 其余j-1个1 { int tmp = lcm( f[i-k][j-1], k ); if( tmp > f[i][j] ) { f[i][j] = tmp; d[i][j] = i-k; //保存k, 则当前因子为i-k,记忆路径 } } if( f[i][j] >= f[i][ f[i][0] ] ) //取等号,最大值相同时,J越大,字典序越小 f[i][0] = j; //存储i时最大值的位置 } } } void dfs( int i, int j ) { if( j == 1 ){ val[size=0] = i; return; } dfs( d[i][j], j-1 ); val[ ++size ] = i-d[i][j]; } int main() { init(); int T, n; scanf("%d", &T); while( T-- ) { scanf("%d", &n); printf("%d", f[n][ f[n][0] ] ); dfs( n, f[n][0] ); //通过递归得到 n分解为j个数,存储到数组val[]中 size++; sort( val, val+size ); int s = 0; for(int i = 0; i < size; i++) { int num = val[i]; for(int j = s+2; j <= s+val[i]; j++) printf(" %d", j); printf(" %d",s+1); s = s+val[i]; } puts(""); } return 0; }