UVa 307 - Sticks

Contents

  1. 1. Problem
  2. 2. Solution
  3. 3. Code

Problem

題目網址
中文網址

給你一段一段的小棍子,使它們組成長度相同的木棍,求出長度最小為?

Solution

基本上跟 10364 - Square 有點類似,但剪枝的要求更高,拿了不少次 TLE , 2 秒多 AC ,最後又修改了一番才加速到 0.1 秒。
以下幾個要處理的部分:

  1. 排序後,從長的開始組,減少回溯次數
  2. 組成的棍子長度必定大於等於小段棍子中最長的,且為全部棍子相加後的因數
  3. 要組成一根完整的棍子時的第一段,此時還沒有替這根組任何小段棍子,也就是長度為 0 。如果剩下沒使用的第一根小棍子不能組,接下來也不會行,因為第一根會剩下來。
  4. 一樣長度且沒有用到,可以跳過
  5. 如果現在長度可以完成一根完整的棍子,但遞迴還是失敗,代表之後一定會有無法組完的。此時就算你用別段小棍子完成這根棍子,也無法成功組完每一段小棍子,所以可以提早結束這種長度。

第 5 點影響效率蠻大的,拉快了不少時間。
以上的解釋有些可能不太清楚(有錯就抱歉了…),可以自行揣摩看看。

Code

UVa 307UVa 307 - Sticks
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include<cstdio>
#include<algorithm>
#define N 100
using namespace std;

bool isUsed[N];
int sticks[N],n;
bool backtracking(int idx, int count, int now, int len);
int main()
{
int i;
while (scanf("%d", &n) && n)
{
int all = 0;
for (i = 0; i < n; i++)
{
scanf("%d", &sticks[i]);
all += sticks[i];
}

//從大的開始接,減少回溯次數
sort(sticks, sticks + n, [](const int& a, const int& b)->bool{return a > b; });
fill(isUsed, isUsed + n, false);

int len;
//完整棍子的長度最小為最長的切後棍子。
for (len = sticks[0]; len <= all; len++)
{
if (all%len)//一定為總長的因數,因為完整的棍子長度皆相同
continue;

if (backtracking(0, 0, 0, len))
break;
}

printf("%d\n", len);
}

return 0;
}
bool backtracking(int idx, int count, int now, int len)//(目前index,已使用棍子數,目前長度,目標長度)
{
//完成一根完整的棍子
if (now == len)
{
//所有切後的棍子都用完,且長度達到 len
if (count == n)
return true;
now = 0;
}

if (!now)
{
//要組成完整棍子的第一根,此時總長還沒有組任何棍子,長度為 0 (now)。所以如果第一根接上的就不行,接下來也不會行,因為第一根會剩下來。
for (idx = 0; isUsed[idx]; idx++);
isUsed[idx] = true;
if (backtracking(idx + 1, count+1, sticks[idx], len))
return true;
isUsed[idx] = false;
}
else
{
for (int i = idx; i < n; i++)
{
if (isUsed[i] || (i&&sticks[i] == sticks[i - 1] && !isUsed[i - 1]))//一樣的長度,且沒有用到的,不需要再做
continue;

if (now + sticks[i] <= len)
{
isUsed[i] = true;
if (backtracking(i + 1, count+1, now + sticks[i], len))
return true;
isUsed[i] = false;

//代表此長度不管怎麼擺,後來都會有組無法完成
if (now + sticks[i] == len)
return false;
}
}
}

return false;
}