1. 程式人生 > 其它 >AtCoder題解 —— AtCoder Beginner Contest 185 —— C - Duodecim Ferra —— 組合數學、揹包

AtCoder題解 —— AtCoder Beginner Contest 185 —— C - Duodecim Ferra —— 組合數學、揹包

技術標籤:OJ題解# AtCoder題解AtCoder題解ABC185C題Duodecim Ferra組合數學

題目相關

題目連結

AtCoder Regular Contest 185 C 題,https://atcoder.jp/contests/abc185/tasks/abc185_c

Problem Statement

There is an iron bar of length L lying east-west. We will cut this bar at 11 positions to divide it into 12 bars. Here, each of the 12 resulting bars must have a positive integer length.

Find the number of ways to do this division. Two ways to do the division are considered different if and only if there is a position cut in only one of those ways.
Under the constraints of this problem, it can be proved that the answer is less than 2^{63}.

Input

Input is given from Standard Input in the following format:

L

Output

Print the number of ways to do the division.

Samples1

Sample Input 1

12

Sample Output 1

1

Explaination

There is only one way: to cut the bar into 12 bars of length 1 each.

Samples2

Sample Input 2

13

Sample Output 2

12

Explaination

Just one of the resulting bars will be of length 2. We have 12 options: one where the westmost bar is of length 2, one where the second bar from the west is of length 2, and so on.

Samples3

Sample Input 3

17

Sample Output 3

4368

Constraints

  • 12≤L≤200
  • L is an integer.

題解報告

題目翻譯

一個長度為 L 的鐵棍,我們將切 11 次,將鐵棍分為 12 個部分,每個部分的長度為正整數。請找出一共有幾種切法。在本問題中,保證答案是小於 2^{63}

題目分析

又是一個數學題。通過不同的角度看問題,我們有不同解法。已知長度為 L,切 11 次。裁點有 L-1 個,我們取其中的 11 個,根據組合答案為 C^{11}_{L-1},這樣問題就變成如何解組合數。

資料範圍估計

L 最大為 200,因此最大的資料為 C^{11}_{199}=\frac{11!}{(199-11)!*199!},需要使用 long long。

遞推

根據組合數學,我們有這樣的遞推關係:C^m_{n}=C^m_{n-1}+C^{m-1}_{n-1}

AC 參考程式碼

//https://atcoder.jp/contests/abc185/tasks/abc185_c
//C - Duodecim Ferra
#include <bits/stdc++.h>

using namespace std;

//如果提交到OJ,不要定義 __LOCAL
#define __LOCAL

typedef long long LL;

const int MAXL=2e2+4;
LL ans[MAXL][MAXL];

int main() {
#ifndef __LOCAL
    //這部分程式碼需要提交到OJ,本地除錯不使用
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#endif
    LL l;
    cin>>l;

    //初始化
    l--;
    for (int i=0; i<=l; i++) {
        ans[i][0]=1;
        ans[i][i]=1;
        ans[i][1]=i;
    }
    //遞推計算
    for (int i=1; i<=l; i++) {
        for (int j=2; j<i; j++) {
            ans[i][j]=ans[i-1][j-1]+ans[i-1][j];
        }
    }

    cout<<ans[l][11]<<"\n";

#ifdef __LOCAL
    //這部分程式碼不需要提交到OJ,本地除錯使用
    system("pause");
#endif
    return 0;
}

時間複雜度

O(L^2)。

空間複雜度

O(L^2)。

改進

通過遞推,我們得到一個 O(L^2) 時間複雜度的演算法。下面我們來討論一下,如何優化演算法。我們還是從數學入手。根據組合定義,我們可以寫出如下計算:

C^{11}_{L-1}=\frac{(L-1)!}{11!*(L-12)!}=\frac{(L-1)*(L-2)*\cdots*(L-11))}{11!}\\=\frac{L-1}{11}*\frac{L-2}{10}*\cdots*\frac{L-11}{1}

這樣我們可以得到以下的改善程式碼。

AC 參考程式碼

//https://atcoder.jp/contests/abc185/tasks/abc185_c
//C - Duodecim Ferra
#include <bits/stdc++.h>

using namespace std;

//如果提交到OJ,不要定義 __LOCAL
//#define __LOCAL

int main() {
#ifndef __LOCAL
    //這部分程式碼需要提交到OJ,本地除錯不使用
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#endif
    long long l;
    cin>>l;

    long long ans=1;
    for (long long i=1; i<=11; i++) {
        ans *= (l-i);
        ans /=i;
    }
    cout<<ans<<"\n";

#ifdef __LOCAL
    //這部分程式碼不需要提交到OJ,本地除錯使用
    system("pause");
#endif
    return 0;
}

時間複雜度

O(1)。

空間複雜度

O(1)。

這樣,我們通過數學優化,將 O(L^2) 時間複雜度優化到 O(1)。

揹包

同樣我們可以用揹包思路。假設 f[i][j] 表示用 i 個正整數湊出 j 的方案數,k 表示長度。遞推的關係為:

f[i][j]=\begin{cases} 1 & \text{ if } i=0,j=0 \\ 0 & \text{ if } j<i \\ f[i][j]+f[i-1][j-k] & \text{ otherwise } \end{cases}

AC 參考程式碼

//https://atcoder.jp/contests/abc185/tasks/abc185_c
//C - Duodecim Ferra
#include <bits/stdc++.h>

using namespace std;

//如果提交到OJ,不要定義 __LOCAL
//#define __LOCAL

typedef long long LL;

const int MAXL=2e2+2;
LL ans[12+2][MAXL];

int main() {
#ifndef __LOCAL
    //這部分程式碼需要提交到OJ,本地除錯不使用
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#endif
    LL l;
    cin>>l;

    //初始化
    ans[0][0]=1;
    //遞推
    for (LL i=1; i<=12; i++) {
        for (LL k=1; k<=l; k++) {
            for (LL j=l; j>=max(k, i); j--) {
                ans[i][j] += ans[i-1][j-k];
            }
        }
    }

    cout<<ans[12][l]<<"\n";

#ifdef __LOCAL
    //這部分程式碼不需要提交到OJ,本地除錯使用
    system("pause");
#endif
    return 0;
}

時間複雜度

O(L^2)。

空間複雜度

O(L)。