作者: | 来源:互联网 | 2023-10-10 12:40
动态规划
集合
f[i][j]f[i][j]f[i][j] 表示填了前 iii 个数,且填出来的排列为 “ jjj 单调序列” 的方案数。(即含有 j−1j-1j−1 个折点)
属性
数量
状态计算
将 iii 填到 1∼i−11∼i−11∼i−1 的一个排列中,折点数量发生的变化。
需列举出$ i $插入到波峰和波谷、最左端点(0)、最右端点(i-1)、单调序列的情况。
下面画图列出:(横线代表节点 iii 在 1∼i−11∼i−11∼i−1 序列中可插入的位置,蓝线标记递减序列,红线标记递增序列)
注: 为了更方便直观的展示,波峰和波谷中的节点不严格按大小绘图。
插入到单调序列中,折点数量+2,增加波峰和波谷各一个。
插入到波峰最高点的两端或波谷两端的最高点中,折点数量不变。
插入到波谷最低点两端中,折点数量+2。插入到波峰两端的最低点中涉及重复情况,不作绘图。
插入到最左端点(0),折点数量+1。最右端点(i-1) 同理。
因此,至多会多出两个折点。则状态转移方程应为:
f[i][j]=f[i−1][j]∗x+f[i−1][j−1]∗y+f[i−1][j−2]∗zf[i][j] = f[i - 1][j] * x + f[i - 1][j - 1] * y + f[i - 1][j - 2] * zf[i][j]=f[i−1][j]∗x+f[i−1][j−1]∗y+f[i−1][j−2]∗z
下面求x,y,z.
显而易见,
x=jx=jx=j,即 放入 iii 后,放入i后,使排列折点不增加的数量,就是 原排列中折点数量+1。
y=2y=2y=2,即 放入 iii 后,使排列中折点数量增加1的数量 = 2
根据上面的结论,我们知道将 iii 放入这个序列后,有 j−2j-2j−2 种放置方式使该排列折点数不变,有 2 种放置方式使该排列中折点数加一。
而将 iii 放入这 i−1i-1i−1 个数中,总共有 iii 种放法。
所以放入之后使其折点数量+2 的放法有 i−(j−2)−2=i−ji-(j-2)-2=i-ji−(j−2)−2=i−j 种。
故 z=(i−j)z=(i-j)z=(i−j)。
转移方程:
f[i][j]=f[i−1][j]∗j+f[i−1][j−1]∗2+f[i−1][j−2]∗(i−j)f[i][j] = f[i - 1][j] * j + f[i - 1][j - 1] * 2 + f[i - 1][j - 2] * (i - j)f[i][j]=f[i−1][j]∗j+f[i−1][j−1]∗2+f[i−1][j−2]∗(i−j)
部分文字参考了 @垫底抽风 的题解。
C++ 代码
#include using namespace std;const int N = 520;
const int mod = 123456;int n, k;
int f[N][N];int main()
{cin >> n >> k;f[1][1] &#61; 1;f[2][1] &#61; 2;for (int i &#61; 3; i <&#61; n; i&#43;&#43;)for (int j &#61; 1; j <&#61; k && j <&#61; i; j&#43;&#43;){(f[i][j] &#43;&#61; f[i - 1][j] * j) %&#61; mod;(f[i][j] &#43;&#61; f[i - 1][j - 1] * 2) %&#61; mod;if (j > 1) (f[i][j] &#43;&#61; f[i - 1][j - 2] * (i - j)) %&#61; mod;}cout << f[n][k];return 0;
}