題目:已知一些等長的木棍切割后的小段,問能拼成最小的原始木棍組的長度。
分析:圖論、搜索、剪枝。回溯解題,不過需要很多剪枝。
1.遞減排序小段減少搜索次數,并為其他的優化最準備;
2.相同長度的前面小段沒有取,本段取了也沒用;
3.一根木棍構成失敗,後面也不成立,那麼直接結束;
4.搜索同一根木棍時,只會取當前取得的小段後面的小段;
說明:最開始以小段為基準不好剪枝,改成以木棍為基準╮(╯▽╰)╭。
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
using namespace std;
int stick[101], units[101], visit[101];
int dfs(int unintnow, int unintsize, int sticknow, int sticklen, int sticksize)
{
if (sticknow >= sticksize) return 1;
for (int i = unintnow; i < unintsize; ++ i) {
//1: 相同的前面不取,这个也不取
if (visit[i] || (i && !visit[i-1] && units[i-1] == units[i])) continue;
if (stick[sticknow]+units[i] > sticklen) continue;
visit[i] = 1;
stick[sticknow] += units[i];
if (stick[sticknow] == sticklen) {
if (dfs(0, unintsize, sticknow+1, sticklen, sticksize)) return 1;
}else if (stick[sticknow] < sticklen) {
if (dfs(i+1, unintsize, sticknow, sticklen, sticksize)) return 1;
}
stick[sticknow] -= units[i];
visit[i] = 0;
//2: 拼第sticknow根时失败,则直接结束
if (stick[sticknow]+units[i] == sticklen || !stick[sticknow]) return 0;
}
return 0;
}
bool cmp(int a, int b)
{
return a > b;
}
int main()
{
int n;
while (cin >> n && n) {
int sum = 0, max = 0;
for (int i = 0; i < n; ++ i) {
cin >> units[i];
sum += units[i];
if (max < units[i])
max = units[i];
stick[i] = 0;
visit[i] = 0;
}
sort(units, units+n, cmp);
for (int l = max; l <= sum; ++ l)
if (sum%l == 0 && dfs(0, n, 0, l, sum/l)) {
cout << l << endl;
break;
}
}
return 0;
}
測試數據:
64
40 40 30 35 35 26 15 40 40 40 40 40 40 40 40 40 40 40 40 40 40
40 40 43 42 42 41 10 4 40 40 40 40 40 40 40 40 40 40 40 40 40
40 25 39 46 40 10 4 40 40 37 18 17 16 15 40 40 40 40 40 40 40
40