1. 程式人生 > 實用技巧 >DP搬運工2

DP搬運工2

DP搬運工2

題目描述

給你 \(n,K\),求有多少個 \(1\)\(n\) 的排列,恰好有 \(K\) 個數 \(i(1<i<n)\) 滿足 \(a_{i-1},a_{i+1}\) 都小於 \(a_i\)

輸入格式

一行兩個整數 \(n,K\)

輸出格式

一行一個整數 \(ans\) 表示答案 \(mod\ 998244353\)

樣例

樣例輸入1

4 1

樣例輸出1

16

樣例輸入2

10 3

樣例輸出2

1841152

資料範圍與提示

對於 \(25\%\) 的測試點,\(1\leqslant n,K \leqslant 10\)

對於 \(50\%\)

的測試點,\(1\leqslant n,K \leqslant 100\)

對於 \(100\%\) 的測試點,\(1\leqslant n,K \leqslant 2000\)

保證資料有梯度

分析

同樣的預設型 \(dp\) ,但是要簡單一些。我們這次只需要考慮有幾個符合條件即可。所以我們定義 \(f[i][j]\) 為插入 \(i\) 的時候,有 \(j\) 個滿足要求的數。我們仍然按照插入位置分情況。

轉移肯定是從 \(f[i-1][j]\) 轉移來,那麼我們考慮一下,什麼情況下加一個數但是符合要求的數量不變:

  • 1、放到之前符合要求的某一組裡,多了一組少了一組,相當於沒變化。
  • 2、放到兩個端點的時候不變。

其他情況都是不加的。然後愉快的轉移

程式碼

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn = 2e3+10;
const int mod = 998244353;
int n,k;
int f[maxn][maxn];
int main(){
	scanf("%d%d",&n,&k);
	//初始化
	f[1][0] = 1;
	f[2][0] = 2;
	for(int i=3;i<=n;++i){
		int jl = min(k,i>>1);//最多有i除以2的滿足的情況。
		for(int j=0;j<=jl;++j){//列舉所有的位置
			if(f[i-1][j] == 0)continue;
			f[i][j] = (f[i][j] + f[i-1][j] * 1LL * ((j+1) << 1))%mod;//放到之前的某個裡或者邊界,此時滿足條件的不加
			f[i][j+1] = (f[i][j+1] + f[i-1][j] * 1LL * (i-((j+1) << 1)))%mod;//放到其他地方,滿足條件的加一
		}
	}
	printf("%d\n",f[n][k]);
	return 0;
}