poj3590 The shuffle Problem(置换+dp)
2018-02-05 07:17
387 查看
题目链接
题目大意:一个置换多次操作后就可以回到最初的状态,这个次数称为置换的循环节。求长度为n的序列的最大的循环节x,并且构造循环节为x的字典序最小的方案。
分析:
这道题是bzoj1025的变式
如果我们已知一个置换
我们通过找到ta的轮换就可以计算出ta的循环节:lcm(size轮换)lcm(size轮换)
我们要使长度为n的置换的循环节尽可能长
就需要产生一种分割方式,把序列分成若干端(若干轮换),使得所有部分长度的lcm最大
上述问题是可以用dp解决的
已知循环节以及分割方式,我们就可以构造解了
直接贪心的把分割长度从小到大放到原始的递增序列上,错一位即可
下面我们就讨论一下dp的过程:
一开始我想的比较简单:f[i]f[i]表示数ii的最大分割方案
f[i]=max(f[i],lcm(f[j],i−j))f[i]=max(f[i],lcm(f[j],i−j))
再开一个数组gg记录每个状态的转移点,最后就可以倒退得到最大分割方案了
但是为什么WA了呢?
这样dp虽然可以计算出正确的循环节长度
但是得到的分割方案有可能段数较少,长度较大,得到的解不是字典序最小
我们要保证求得最小公倍数之后置换排序最小
考虑lcmlcm最简单的定义:
lcm(a,b)=pk11∗pk22∗pk33∗...∗pkmmlcm(a,b)=p1k1∗p2k2∗p3k3∗...∗pmkm
其中pipi是指数,ki=max(kai,kbi)ki=max(kai,kbi)
也就是说我们将lcm质因数分解了
pkiipiki都是互素的,所以我们可以把序列分割成长度为pkiipiki的轮换,同时保证∑pkii=n∑piki=n
设计状态:f[i][j]f[i][j]表示使用了前ii个素数,当前pkpk和为jj的lcm最大值
(100以内一共有25个素数;pkpk实际上就是一个轮换的长度,那么jj就是序列的长度了)
转移:f[i][j]=max(f[i−1][j],f[i−1][k]∗prime[i]j−k)f[i][j]=max(f[i−1][j],f[i−1][k]∗prime[i]j−k)
用另一个数组记录每一个状态的转移点
这样我们就可以把lcm分解成长度分别是为pk11,pk22,pk33,...,pkmmp1k1,p2k2,p3k3,...,pmkm的若干轮换
质数pi,p2i,p3i,...,pnipi,pi2,pi3,...,pin当做同组的物品,
对于同组的物品只能选一个或者都不选,总和为nn的最大值
虽然我们可以在f[tot]
处的到最大循环节
但是有些素数是我们没有用上的,
如果g[i][j]=0g[i][j]=0,就说明这个数没有用
我们在构造解的时候,可能会发现:用的数的pkiipiki之和不到n,
这时我们就用长度为1的轮换补充即可
题目大意:一个置换多次操作后就可以回到最初的状态,这个次数称为置换的循环节。求长度为n的序列的最大的循环节x,并且构造循环节为x的字典序最小的方案。
分析:
这道题是bzoj1025的变式
如果我们已知一个置换
我们通过找到ta的轮换就可以计算出ta的循环节:lcm(size轮换)lcm(size轮换)
我们要使长度为n的置换的循环节尽可能长
就需要产生一种分割方式,把序列分成若干端(若干轮换),使得所有部分长度的lcm最大
上述问题是可以用dp解决的
已知循环节以及分割方式,我们就可以构造解了
直接贪心的把分割长度从小到大放到原始的递增序列上,错一位即可
example: a: 1 2 3 4 5 分割方式: 1 2|3 4 5 ans: 2 1 4 5 3
下面我们就讨论一下dp的过程:
一开始我想的比较简单:f[i]f[i]表示数ii的最大分割方案
f[i]=max(f[i],lcm(f[j],i−j))f[i]=max(f[i],lcm(f[j],i−j))
再开一个数组gg记录每个状态的转移点,最后就可以倒退得到最大分割方案了
但是为什么WA了呢?
这样dp虽然可以计算出正确的循环节长度
但是得到的分割方案有可能段数较少,长度较大,得到的解不是字典序最小
实际上,题目可以转化为:
给你一个整数n,求a1+a2+a3...+am=na1+a2+a3...+am=n, 并且a1,a2,...,ama1,a2,...,am的最小公倍数最大我们要保证求得最小公倍数之后置换排序最小
考虑lcmlcm最简单的定义:
lcm(a,b)=pk11∗pk22∗pk33∗...∗pkmmlcm(a,b)=p1k1∗p2k2∗p3k3∗...∗pmkm
其中pipi是指数,ki=max(kai,kbi)ki=max(kai,kbi)
也就是说我们将lcm质因数分解了
pkiipiki都是互素的,所以我们可以把序列分割成长度为pkiipiki的轮换,同时保证∑pkii=n∑piki=n
设计状态:f[i][j]f[i][j]表示使用了前ii个素数,当前pkpk和为jj的lcm最大值
(100以内一共有25个素数;pkpk实际上就是一个轮换的长度,那么jj就是序列的长度了)
转移:f[i][j]=max(f[i−1][j],f[i−1][k]∗prime[i]j−k)f[i][j]=max(f[i−1][j],f[i−1][k]∗prime[i]j−k)
用另一个数组记录每一个状态的转移点
这样我们就可以把lcm分解成长度分别是为pk11,pk22,pk33,...,pkmmp1k1,p2k2,p3k3,...,pmkm的若干轮换
注意
这样就转化成了一个特殊的分组背包质数pi,p2i,p3i,...,pnipi,pi2,pi3,...,pin当做同组的物品,
对于同组的物品只能选一个或者都不选,总和为nn的最大值
for 所有的组k for v=V..0 for 所有的i属于组k f[v]=max{f[v],f[v-c[i]]+w[i]}
tip
在代码中,如果我们不选一个数:f[i][j]=f[i-1][j];g[i][j]=0;
虽然我们可以在f[tot]
处的到最大循环节
但是有些素数是我们没有用上的,
如果g[i][j]=0g[i][j]=0,就说明这个数没有用
我们在构造解的时候,可能会发现:用的数的pkiipiki之和不到n,
这时我们就用长度为1的轮换补充即可
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=102; int sshu[26]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,57,61,67,71,73,79,83,89,97}; int n,f[30] ,g[30] ,a ; void solve() { memset(f,0,sizeof(f)); f[0][0]=1; memset(g,0,sizeof(g)); for (int i=1;i<=25;i++) for (int j=n;j>=0;j--) //倒序 { f[i][j]=f[i-1][j]; //不用这个素数 int now=sshu[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 这个轮换的长度 now*=sshu[i]; } } int ans=0,t=0; for (int i=n;i>=0;i--) if (f[25][i]>ans) ans=f[25][i],t=i; printf("%d ",ans); int cnt=0,x=25,one,tt=0; while (t) { if (!g[x][t]) x--; else a[++cnt]=g[x][t],t-=g[x][t],x--,tt+=a[cnt]; } one=n-tt; //长度为1的轮换 sort(a+1,a+1+cnt); for (int i=1;i<=one;i++) printf("%d ",i); int s=one+1; for (int i=1;i<=cnt;i++) { for (int j=1;j<a[i];j++) printf("%d ",j+s); printf("%d ",s); s+=a[i]; } printf("\n"); } int main() { int T; scanf("%d",&T); while (T--) { scanf("%d",&n); solve(); } return 0; }
相关文章推荐
- POJ-3590 The shuffle Problem 置换+DP | DFS
- POJ 3590 The shuffle Problem(置换+DP)
- poj 3590 The shuffle Problem(置换群+DP)
- POJ 3590 The shuffle Problem [置换群 DP]
- poj 3590 The shuffle Problem (置换+分组背包)
- POJ 3590 The shuffle Problem
- 【POJ 3590】The shuffle Problem
- [POJ3590]The shuffle Problem(置换+dp)
- poj 3590 The shuffle Problem 置换群+DP
- POJ3590 The shuffle Problem——置换群+DP/递推预处理
- [POJ 3590]The shuffle Problem
- poj 3590 The shuffle Problem(置换群的幂运算)
- poj 3590 The shuffle Problem
- poj 2282 The Counting Problem && poj 3286 How many 0's? (数位dp)
- POJ 2282 The Counting Problem (数位dp)
- poj 2282 The Counting Problem (数位DP)
- pku3590 The shuffle Problem
- poj The shuffle Problem
- poj 2904 The Mailboxes Manufacturers Problem( 区间dp)
- pku 3590 The shuffle Problem