1. 程式人生 > 實用技巧 >排隊

排隊

題目大意

*小花所在的班有 n名同學(任何兩位同學身高不相同),正準備排成一列縱隊,但他們不想按身高從矮到高排,那樣太單調,太沒個性。
*他們希望恰好有 k對同學是高的在前,矮的在後,其餘都是矮的在前,高的在後。如當 n=5,k=3 時,假設5 人從矮到高分別標為 1,2,3,4,5,則 (1,5,2,3,4),(2,3,1,5,4),(3,1,4,2,5)都是可行的排法。
*小花想知道總共有多少種可行排法。

輸入格式

  • 一行兩個整數 n 和 k,意義見問題描述。

輸出格式

  • 輸出一個整數,表示可行排法數。由於結果可能很大,請輸出排法數 mod 1799999 的值。

樣例

樣例輸入

5 3

樣例輸出

15

演算法分析:

  • 這nm是dp?
  • 這nm還真是dp(以為是數論直接跳了……)
  • 這個是可以用遞推做出來的 用f[i][j]表示前i個人有j對逆序對(不懂的去百度吧)
  • 初始化f[i][0] = 1(即正好是順序排列)
  • 然後我們去推第i個數 想象如果第i個數最大放在最後面 就一個不會加 放在倒數第二個呢 就會加一個 依次類推……
  • 然後就有了動態轉移方程了f[i][j] = f[i-1][j] + f[i-1][j-1] + ... + f[i-1][j-i+1]
  • 用j-1代換j 則: f[i][j-1] = f[i-1][j-1] + f[i-1][j-2] + ... + f[i-1][j-i]
  • 用上面的式子減下面的式子 得:f[i][j] = f[i][j-1] + f[i-1][j] - f[i-1][j-i]
  • 然後就可以碼程式碼啦

程式碼展示

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 105,mod = 1799999;

int n,k,sum;
ll f[maxn][maxn*maxn];

int main(){
	scanf("%d%d",&n,&k);
	for(int i = 1;i <= n;++i)f[i][0] = 1;
	for(int i = 1;i <= n;++i){
		sum += i - 1;//優化 前i個最多出現sum個逆序對
		for(int j = 1;j <= k;++j){
			if(j > sum)break;
			f[i][j] = (f[i][j-1] + f[i-1][j]) % mod;
			if(i <= j)
				f[i][j] = (f[i][j] - f[i-1][j-i] + mod) % mod;//防止相減出現負數
		}
	}
	printf("%lld\n",f[n][k] % mod);
	return 0;
}