對於自然數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上升段中呢?分析如下
令表示的全排列劃分爲段的數目,從上面我們知道肯定是可以從添加一個元素得到的,那麼他的劃分成段的數目和種的誰有關係呢,無非就兩個即
- ,即得上升段的數目,他如何影響,也就是說有幾種插入的方式,使其增加一個上升段?比如
3,1,2,4
是的一個組合,我們有三種插入5的方式,第一種:5,3,1,2,4;
,首先插到頭部一定是一個可行的方案。然後3,1,5,2,4;3,1,2,5,4
,可以看到對每一個段
,我們有段長-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;
}