Sticks
题目链接:
http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=17446
题目大意:
有n根小木棍,长短不一,现在要求你将它们拼成一些长度一样的木棒,问拼成多少长度的木棒,可以得到最多的木棒数。
如样例:
9 5 2 1 5 2 1 5 2 1输出:6
解题思路:
这道题目很容易想到DFS回溯,关键是剪枝
有如下几个剪枝
1.把所有木棍的长度从大到小排列,组合木棒时优先使用长的木棍,这样可以加快组合速度,并且对后面的剪枝有帮助。
2.木棒的长度一定是大于等于最长木棍的长度并且小于等于所有木棍长度的和,这个很容易证明。
3.木棒的长度一定是所有木棍长度的和的约数,这个也很容易证明。
4.在某一个木棒的组合过程中,对于当前的木棍stick[i],如果stick[i-1]没有被组合并且stick[i] == stick[i-1],那么不用考虑stick[i],因为之前已经试过stick[i-1], 发现无法组合,所以stick[i]肯定更加不行。
5.如果此次是在尝试第i个木棒的第一段,假设stick[j]为当前可以被使用的最长的木棍,如果此次组合失败,直接退出搜索,退回到对第i-1个木棒的搜索。因为当stick[j]作为第i根木棒的第一段无法组合的话,作为第i + k (k > 0)的木棒的第一段也不行,因为此时所剩下的木棒更少,为其子集。
6.如果能用一段完成一根木棒,就没必要采用多段来组合,因为永远可以用多段来替代一段,却不能用一段来替代多段。
这道题目的剪枝很经典,是道好题目。
#include<iostream>
#include<fstream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<set>
#include<ctype.h>
#include<algorithm>
#include<string>
#define PI acos(-1.0)
#define maxn 1000
#define INF 1<<25
#define mem(a, b) memset(a, b, sizeof(a))
typedef long long ll;
using namespace std;
int n, a[100], sum, mx, vis[100];
bool cmp(int s, int v)
{
return s > v;
} // (1)
bool dfs(int pos, int tot, int cnt)
{
int i;
if (cnt == mx) return true; // 组合成功
for (i = pos; i < n; i++) if (!vis[i])
{
if (i && !vis[i - 1] && a[i] == a[i - 1]) continue; // (4)
if (tot + a[i] < sum / mx)
{
vis[i] = 1;
if (dfs(i + 1, tot + a[i], cnt)) return true;
vis[i] = 0;
if (tot == 0) return false; // (5)
}
else if (tot + a[i] == sum / mx)
{
vis[i] = 1;
if (dfs(0, 0, cnt + 1)) return true;
vis[i] = 0;
return false; // (6)
}
}
return false;
}
int main ()
{
int i, j;
while(scanf("%d", &n) != EOF)
{
if (!n) break;
sum = mx = 0;
for (i = 0; i < n; i++)
{
scanf("%d", a + i);
sum += a[i];
if (a[i] > mx) mx = a[i];
}
sort(a, a + n, cmp);
mx = sum / mx;
while(mx)
{
if (sum % mx == 0) (3)
{
mem(vis, 0);
if (dfs(0, 0, 0)) break;
}
mx--;
}
printf("%d\n", sum / mx);
}
return 0;
}