AtCoder题解 —— AtCoder Beginner Contest 185 —— C - 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},这样问题就变成如何解组合数。

扫描二维码关注公众号,回复: 12359528 查看本文章

数据范围估计

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)。

猜你喜欢

转载自blog.csdn.net/justidle/article/details/111161222