题意:给出n个木棍,把这n个木棍拼接成m个木棍并且这m个木棍的长度相等。输出拼接之后的木棍最短的长度是多少。
分析:这是一道深搜+剪枝的题目。枚举拼接之后木棍可能的最短长度,最短长度应满足两个要求:
(1)最短长度应大于等于n个木棍中的最长的长度。
(2)最短长度应该是n个木棍的长度之和的约数。
下面就是如何剪枝的问题:
个人认为最重要的剪枝有两个:
(1)当搜索一个木棍i时,假设当前选择木棍i不能拼接成m个长度相等的木棍,那么后面的和木棍i长度相等的木棍必然也不是能够选择的木棍。
(2)这个是最重要的一个剪枝,假设选择木棍i作为拼接之后木棍的第一节,如果木棍i不能满足要求,那么我们就需要返回上一个函数。因为木棍i一定是要作为某一个拼接之后的木棍中的一节的,而我们选择它作为拼接之后的木棍的第一节都无法满足要求。那么说明在剩余的木棍中,木棍i是不能被使用的。所以这个时候我们只能选择回溯。
下面是代码:
#include<iostream>
#include<math.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <algorithm>
using namespace std;
const int maxn = 100+10;
int a[maxn], num_sticks;
bool visit[maxn], flag = false;
bool cmp(int x, int y)
{
return x > y;
}
void dfs(int cur_stick, int cur_len, int len, int n)
{
if(cur_len == len)
{
cur_len = 0;
if(cur_stick == num_sticks)
{
flag = true;return;
}
}
//if(cur_stick == num_sticks){flag = true;return;}
for(int i = 1; i <= n; i++)
{
if(!visit[i] && cur_len+a[i] <= len)
{
visit[i] = true;
if(cur_len == 0)
dfs(cur_stick+1,cur_len+a[i],len,n);
else
dfs(cur_stick,cur_len+a[i],len,n);
visit[i] = false;
if(flag)return;
if(cur_len == 0)return;
int j = 0;
for(j = i+1; j <= n && a[i] == a[j]; j++);
i = j-1;
}
}
}
int main()
{
int n = 0;
while(scanf("%d",&n) && n)
{
int sum = 0, i = 0;
for(i = 1; i <= n; i++)
{
scanf("%d",&a[i]);
sum += a[i];
}
sort(a+1,a+n+1,cmp);
int ans = a[1];
for(i = a[1];i <= sum; i++)
{
if(sum%i == 0)
{
num_sticks = sum/i;
memset(visit,false,sizeof(visit));
flag = false;
dfs(0,i,i,n);
if(flag)
{
ans = i;break;
}
}
}
printf("%d\n",ans);
}
return 0;
}