K上升段 解題思維詳述

對於自然數1…n的一個排列A[1…N] 可以劃分爲若干個單調遞增序列。每個單調遞增序列由連續元素A[st…ed]組成,且滿足以下條件: 1<=st,ed<=n; A[i] A[ed+1];      例如:排列1 2 4 5 6 3 9 10 7 8 可劃分爲3個單調遞增序列 1 2 3 4 5 6;3 9 10;7 8;所以我們稱這是一個3上升段序列 。 現在給定n和k , 求出n的全排列中的,k上升段序列的個數。


挺好一道題,這道題基本的思路很容易想出來,暴力枚舉每一個排列數,然後計算他是否是一個k上升段,是的話統計個數,代碼也很好寫,但是隻有20分,

#define MAX 1005
#define inf 0x3fffff
#define ll int
#define vec vector<ll>
#define PA pair<ll,ll>

//判斷v是否滿足k上升段
bool cal(vec v, ll k) {
	ll cnt = 1, l = v.size();//統計上升段的個數
	for (int i = 1; i < l; i++) {
		if (v[i] <= v[i - 1])cnt++;
	}
	if (cnt == k)return 1;
	else return 0;
}

int main() {
	ll n, k;
	while (cin >> n >> k) {
		vec v(n);
		for (int i = 0; i < n; i++) v[i] = i + 1;
		ll res = cal(v, k);
		while (next_permutation(v.begin(), v.end()))
			res += cal(v, k);
		cout << res << endl;
	}
}

我們需要知道全排列有多少種,

4位:10000種
5位:100000種
6位:1000000種
7位:10000000種
8位:100000000種

顯然枚舉只能拿到前幾種的分數,但是我們可以注意到,4的全排列和3的全排列是有關係的,比如對於1,2,3,我們有4個位置插入4,他就會變成4的四種排列方案,這種關係如何應用到求k上升段中呢?分析如下

dp[i][j]dp[i][j]表示ii的全排列劃分爲jj段的數目,從上面我們知道dp[i]dp[i]肯定是可以從dp[i1]dp[i-1]添加一個元素ii得到的,那麼他的劃分成jj段的數目和dp[i1]dp[i-1]種的誰有關係呢,無非就兩個即

  1. dp[i1][j1]dp[i-1][j-1],即i1i-1j1j-1上升段的數目,他如何影響dp[i][j]dp[i][j],也就是說有幾種插入ii的方式,使其增加一個上升段?比如3,1,2,4dp[4][2]dp[4][2]的一個組合,我們有三種插入5的方式,第一種:5,3,1,2,4;,首先插到頭部一定是一個可行的方案。然後3,1,5,2,4;3,1,2,5,4,可以看到對每一個,我們有段長-1種插入方式都可以使得dp[i1][j1]dp[i-1][j-1]進化成dp[i][j]dp[i][j](多寫幾個例子就看出來了)。那麼將所有段加起來我們得到插入方式有段長之和-段的數目,也就是iji-j,再加上插入首部這種方式一共有(ij+1)dp[i1][j1](i-j+1)dp[i-1][j-1]種。
  2. dp[i1][j]dp[i-1][j],即i1i-1jj上升段的數目,他如何影響dp[i][j]dp[i][j],也就是說對每一個dp[i1][j]dp[i-1][j]的組合方案,如何插入一個元素ii並保持上升段數目不變,這裏很明顯,我們只需要在每個上升段的末尾插入ii,因爲ii總是比i1i-1的全排列的所有數都大,因此上升序列總能滿足上升的性質。所以它帶來的組合數的增長爲jdp[i1][j]jdp[i-1][j]
  3. 需要注意一點,任何dp[i][1]=1,dp[i][i]=1dp[i][1]=1,dp[i][i]=1,初始化要注意。最後不要用int。
#define MAX 25
#define inf 0x3fffff
#define ll long long
#define vec vector<ll>
#define PA pair<ll,ll>

int main() {
	ll n, k, dp[MAX][MAX]; cin >> n >> k;
	memset(dp, 0, sizeof(dp));
	for (int i = 1; i <= n; i++)dp[i][1] = dp[i][i] = 1;
	for (int i = 3; i <= n; i++)
		for (int j = 2; j < i; j++)
			dp[i][j] = (i - j + 1)*dp[i - 1][j - 1] + j * dp[i - 1][j];
	cout << dp[n][k] << endl;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章