您的位置:首页 > 其它

排列问题(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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: