題目: UVa - 10721 - Bar Codes

題目說明

組成 bar 的單位為長方條 ( units ),units 的顏色可以是黑色也可以是白色,同一種顏色排在一起視為一個 bar。

BC(n, k, m) 表總共使用 n 個 units,組成 k 個 bar,每個 bar 的寬度不可超過 m 個 units,且第一個 bar 為黑色,能組出的所有情況數

nkm,求 BC(n, k, m)

Input: 每組測資有三個整數,依序為 nkm

Output: 輸出 BC(n, k, m)

解題思路

動態規劃題,由於測資範圍不大,因此先算出所有情況的答案再輸出即可。

定義 dp[n][k][m]BC(n, k, m),先設定初始狀態,由於第一個 bar 必為黑色,因此可以知道,dp[n][1][m] = 1,對於所有 m >= n,也就是只能是黑色的 bar,而 m >= n 是因為當 m < n 時無法使用完 n 個 units,因此為 0。

對於 dp[n][k][m]

  • m > ndp[n][k][m] = dp[n][k][n] 。因為最多只可使用 n 個 units。
  • m <= ndp[n][k][m] = dp[n - 1][k - 1][m] + dp[n - 2][k - 1][m] + … + dp[n - m][k - 1][m]
    可以想像為,在原本的組合下接上新的 bar,如 dp[n - 1][k - 1][m] 為接上一個 unit 的 bar 後的情況數,dp[n - 2][k - 1][m] 為接上兩個 units 的 bar 後的情況數,…,最多只可接上 m 個 units 的 bar。而新接上的 bar 的顏色必定與原本最後一個 bar 的顏色相反,由於只有黑白兩色,因此計算時不需考慮顏色。

參考解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <bits/stdc++.h>

using namespace std;

/*
reference:
https://ppt.cc/fr07lx
https://ppt.cc/fzn4hx
*/

static auto __ = []
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
return 0;
}();

int n, k, m;
long long dp[51][51][51];

void compute()
{
// 當只有一個 bar 時,對於 m >= n,dp[n][1][m] = 1 ( 只有一個黑色的 bar )
for (int n = 1; n <= 50; ++n) for (int m = n; m <= 50; ++m) dp[n][1][m] = 1;
for (int n = 1; n <= 50; n++) for (int k = 1; k <= 50; k++) for (int m = 1; m <= 50; m++)
{
// 由於最多 n 個 units,因此當 m > n 時,dp[n][k][m] = dp[n][k][n],也就是無法再接 bar 上去
if (m > n) { dp[n][k][m] = dp[n][k][n]; continue; }
// 新接上的 bar 寬度為 1 ~ m
for (int i = 1; i <= m; i++) dp[n][k][m] += dp[n - i][k - 1][m];
}
}

int main()
{
compute();
while (cin >> n >> k >> m) cout << dp[n][k][m] << '\n';
}

參考資料

[UVA] 10721 - Bar Codes | 水泥城式
UVa/UVa 10721 - Bar Codes.cpp at master · ajahuang/UVa