排列问题(DP)
2018-03-12 09:39
260 查看
题意
长度为N的排列是一个序列(a,a,...,a),恰好包含从1到N的每一个数字。例如,(3,1,4,5,2)是一个长度为5的排列。
对于两个排列a和b,定义
magic(a,b)=max(a,b)+max(a,b)+...+max(a,b)
给定整数N和K,求有多少对排列a和b满足a和b的长度都为N,并且magic(a,b)≥K。
输入格式
2个整数N和K
输出格式
1个整数,表示答案,答案模1,000,000,007
输入样例
2 4
输出样例
2
题解
感谢氧化钠大佬的讲解。举个例子:
132231
最后结果为3+2+3=8,数列中总是大的数覆盖小的数。
考虑从大到小把数放进去,有3种情况:
(x表示填过的其它更大的数)
1.后面需要被覆盖的数增加一个(如添加y,(y<x))
yy xxxx......
需要被覆盖的数+1,总的权值+2y。
2.所填的数一个被覆盖,另一个覆盖后面的数;或两个数在同一位置
xyy xxxxx......
或
x yy xxxxx......
需要被覆盖的数不变,总权值+y。
3.所填的数全部被更大的数覆盖
xy yxxxxx......
需要被覆盖的数−1,总的权值不变。
DP定义状态当前从大到小处理到k,总和为j,有p个数被覆盖的方案数
转移时要利用可以摆放的位置个数,如:
当前数k被大的数覆盖:p个位置;
当前数k随便放一个空位:k个位置;
当前数k要覆盖更小的数:k−p个位置;
于是情况1方案数需 ×(k−p)×(k−p−1),(-1表示减去两个数放同一位置)
情况2方案数需 ×(p×(k−p)+(k−p)×(p+1))
(一个被覆盖时,另一个不能被覆盖+一个没有覆盖时,另一个被覆盖或重合)
情况3方案数需 ×p×p
注:这里的p指转移前状态的p,实际代码还需修改
dp转移即为:
if(j-2*k>=0&&p>0) dp[k][j][p]+=dp[k+1][j-2*k][p-1]*(k-p+1)*(k-p);//情况1 if(j-k>=0) dp[k][j][p]+=dp[k+1][j-k][p]*(p*(k-p)+(k-p)*(p+1));//情况2 dp[k][j][p]+=dp[k+1][j][p+1]*(p+1)*(p+1);//情况3
一些思考过程
首先想到的是把一个串固定为1,2,3,4...n,另一个串排列,答案×n!于是dp[i][j]表示前i个位置magic值为j,显然无法转移,无法判断已经使用过的数,或者哪些被覆盖,max到底为哪个数。
于是转换方向,发现结果实际为1,2..n中一些数计算两次,一些数计算一次,一些数计算零次,于是变为dp[i][j][k]表示还要计算i次数,和为j,当前考虑到数k;
由于无法判断还需要几个数被前面的覆盖,于是添加状态p表示剩余几个数需要被覆盖;
为保证后面的数一定被之前的数覆盖,k变为倒序枚举,并将循环顺序提至第二位
dp变为dp[i][k][j][p]表示还要计算i次数,当前考虑k,和为j,剩余p个需要被覆盖。
显然还要超时,发现i可以通过k与p计算出,于是删除这个状态,变为dp[k][j][p]
转移时仍按照固定一个串的方式转移,发现无法分辨情况2中的两种情况,于是改为同时往两个串添加数,转移成功。
完整代码
#include<cstdio> #include<cstring> const long long MAXN=55,MAXK=2505,MOD=1000000007; long long dp[2][MAXK][MAXN];//当前考虑k,总和为j,有p个数被覆盖 int main() { long long n,K; scanf("%I64d%I64d",&n,&K); dp[(n+1)%2][0][0]=1; for(long long kk=n,k=n%2;kk>=1;kk--,k^=1) { memset(dp[k],0,sizeof(dp[k])); for(long long j=0;j<MAXK;j++) for(long long p=0;p<=kk;p++) { if(j-2*kk>=0&&p>0) dp[k][j][p]=(dp[k][j][p]+(1LL*dp[k^1][j-2*kk][p-1]*(kk-p+1)*(kk-p))%MOD)%MOD; if(j-kk>=0) dp[k][j][p]=(dp[k][j][p]+(1LL*dp[k^1][j-kk][p]*(p*(kk-p)+(kk-p)*(p+1))))%MOD; dp[k][j][p]=(dp[k][j][p]+(1LL*dp[k^1][j][p+1]*(p+1)*(p+1))%MOD)%MOD; } } long long ans=0; for(long long j=K;j<MAXK;j++) ans=(ans+dp[1][j][0])%MOD; printf("%I64d\n",ans); }
相关文章推荐
- 【XSY2666】排列问题 DP 容斥原理 分治FFT
- light oj 1021 - Painful Bases(状压DP解决全排列余数为定值的问题)
- #NOIP模拟赛#排列问题(DP)
- NYOJ 469 擅长排列的小明 II (dp问题)
- 2015寒假集训--dp--数字三角形问题
- 递归算法之排列问题
- 面试100题系列之5字符串的排列组合问题
- BZOJ1072: [SCOI2007]排列perm 状压DP
- hdu2554N对数的排列问题
- 063_多重部分和问题(DP)
- 全错位排列 配对概率问题
- hdu 4975 最大流解决行列和求矩阵问题,用到矩阵dp优化
- poj-1836-Alignment-dp-最长最短子序列问题
- HDU 2201 熊猫阿波 数学排列问题
- 全排列问题&&组合问题
- 随机排列问题
- 排列数字问题
- Piggy-Bank(dp完全背包问题)
- 快餐问题(dp好题)
- 哈理工 2005 排列问题(STL中的全排列)