Description
小叶子的桌面上有 n 本高度不相同的书,n+e 现在需要把这些书按照一定的顺序摆放好。假设第 i 本书的高度为 h[i],n+e
的摆放用一个 1~n的排列 pi 来表示。定义一个摆放的混乱程度:|h[p2]-h[p1]|+|h[p3]-h[p2]|+…
…+|h[pn]-h[pn-1]|,即相邻两本书的高度差的绝对值之和。已知合法的摆放要求其混乱程度不超过 L。小叶子想 要知道,n+e
到底有多少种合法的摆放的方法呢?作为将要参加 NOI 的选手,你应该知道,小叶子只关心这个数 对10^9+7 取模的结果。
Input
第一行两个数 n,L。接下来一行 n 个数 h[i] 1<=n<=100,1<=L,h[i]<=1000
Output
输出一行,表示方案数对 10^9+7 取模的结果。
Sample Input
3 2
2 3 4
Sample Output
2
HINT
【样例解释】
两种合法的摆放姿势如下:
2 3 4
4 3 2
题解
其实应该先写4498的qaq
这题类似那题的思路,可以先写出一个n2∗n∗Ln^2*n*Ln2∗n∗L的方法
原因是贡献有正有负,无法保证第三维的上界仅仅是LLL
那这里的话其实可以把相邻两个数的贡献拆开
比如有三个数a<b<ca<b<ca<b<c,其中位置是acbacbacb
那么在算ababab的时候,我们可以在这一部分先把b−ab-ab−a的贡献加上
那就相当于在下一步插入这个ccc的时候,我们的aaa已经被拔高到bbb这个高度了
并且我们需要保证,无论在任何一段插入这个ccc,相邻的两个数都被拔高到bbb这个高度了
显而易见这样可以保证贡献都是正数,那么第三维就有上界了
设dp[i][j][k][l]dp[i][j][k][l]dp[i][j][k][l]表示插入第iii个数,一共分成了jjj个段,kkk个边界可以放置,贡献至少为lll的方案数
从小到大排序,当插入第iii个数,当前有jjj个段,边界有kkk个的时候
显而易见他的贡献就是(h[i]−h[i−1])∗(2∗(j−1)+k)(h[i]-h[i-1])*(2*(j-1)+k)(h[i]−h[i−1])∗(2∗(j−1)+k)
因为他要把能插入的段的两个端点都拔高到h[i]h[i]h[i]来保证接下来的贡献都是h[i+1]−h[i]h[i+1]-h[i]h[i+1]−h[i]
转移考虑新建一个段作为边界
在某一段旁边添加一个数作为边界
新建一个段
在某一段旁边添加一个数
连接两个段
五种转移即可
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline LL read()
{
LL f=1,x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int stack[20];
inline void write(int x)
{
if(x<0){putchar('-');x=-x;}
if(!x){putchar('0');return;}
int top=0;
while(x)stack[++top]=x%10,x/=10;
while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(int x){write(x);putchar('\n');}
const LL mod=1e9+7;
const int MAXN=105;
const int MAXL=1005;
int f[2][3][MAXN][MAXL],n,L,a[MAXN];
void ad(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int main()
{
n=read();L=read();
if(n==1)return puts("1"),0;
for(int i=1;i<=n;i++)a[i]=read();
sort(a+1,a+1+n);
f[0][2][1][0]=1;f[0][1][1][0]=2;
int nw=0;
for(int i=2;i<=n;i++)
{
nw^=1;memset(f[nw],0,sizeof(f[nw]));
for(int j=1;j<=i;j++)
for(int k=0;k<3;k++)
for(int l=0;l<=L;l++)if(l+(a[i]-a[i-1])*(2*(j-1)+k)<=L&&f[nw^1][k][j][l])//有j段数 总共j-1个地方填数
{
int nxt=l+(a[i]-a[i-1])*(2*(j-1)+k);
if(k)
{
ad(f[nw][k-1][j+1][nxt],1LL*f[nw^1][k][j][l]*k%mod);//新建一段作为边界
ad(f[nw][k-1][j][nxt],1LL*f[nw^1][k][j][l]*k%mod);//在某一段处添加一个边界
}
ad(f[nw][k][j+1][nxt],1LL*f[nw^1][k][j][l]*(j-1+k)%mod);//新建一段
ad(f[nw][k][j][nxt],1LL*f[nw^1][k][j][l]*(2*j-2+k)%mod);//在某一段侧加一个数
ad(f[nw][k][j-1][nxt],1LL*f[nw^1][k][j][l]*(j-1)%mod);//连接两段
}
}
int ans=0;
for(int i=0;i<=L;i++)ad(ans,f[nw][0][1][i]);
pr2(ans);
return 0;
}

探讨了在限制条件下,如何使用动态规划算法求解书本摆放方案数的问题。通过分解相邻书本高度差的贡献,引入边界和段的概念,实现了有效计算方案数量的算法。
343

被折叠的 条评论
为什么被折叠?



