思路:对可行长度进行枚举,通过DFS判断该长度是否可行。由于要求最小可行解,所以从小到大枚举。
对于这里的DFS,有许多可以剪枝和优化的地方,值得学习:
(1)将棒的长度从大到小排列。事实上,如果按小棍长度从大到小进行测试,如果某长度恰好能填满可行解len,那么如果使用更长的小棍,长度显然会超过len,显然不行。如果使用更短的小棍,即使存在某个也能填满len的方案,该方案也不比使用长小棍更有可能获得可行解。
(2)令小棍总长度sum_len,当前枚举长度为test_len,如果sum_len mod test_len != 0,则剪枝。
(3)如果已经找到一个可行解,则对以后的所有情况剪枝。
(4)在填充len时,如果前一个小棍没有用过且当前小棍与前一个小棍长度相等,则剪枝。这里是防止重复测试。
(5)如果第一根小棍不能拼接成len,则剪枝。这个剪枝威力巨大。。我从TLE直接AC了。
事实上可能还有别的剪枝,因为我的程序运行了16MS,还没有到0MS秒杀的地步。而且我的程序对某些变态数据也有心无力。
变态数据:
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
答案为:454。
运行时间:5.5s+
#include <iostream>
#include <algorithm>
using namespace std;
struct Stick
{
int len;
bool have_used;
}stick[80];
int num,sum_len,max_len,piece_num,piece_len;
bool flag;
bool compare(struct Stick a,struct Stick b)
{
return a.len>b.len;
}
void dfs(int pos,int len_now)
{
int i;
if (flag)
return ;
if (piece_num==0)
{
flag=true;
return ;
}
if (len_now==piece_len)
{
piece_num--;
dfs(0,0);
piece_num++;
return ;
}
for (i=pos;i<num;i++)
{
if (flag)
return ;
if (len_now+stick[i].len>piece_len)
continue;
if (stick[i].have_used)
continue;
if (i>0 && stick[i-1].have_used==false && stick[i].len==stick[i-1].len)
continue;
stick[i].have_used=true;
dfs(i+1,len_now+stick[i].len);
stick[i].have_used=false;
if (pos==0)
return ;
}
}
int main()
{
int i;
while (scanf("%d",&num)==1 && num!=0)
{
for (i=0,sum_len=0,max_len=-1;i<num;i++)
{
scanf("%d",&stick[i].len);
stick[i].have_used=false;
sum_len+=stick[i].len;
}
sort(stick,stick+num,compare);
max_len=stick[0].len;
for (i=max_len,flag=false;i<=sum_len && flag==false;i++)
{
if (sum_len%i!=0)
continue;
piece_len=i;
piece_num=sum_len/i;
dfs(0,0);
}
printf("%d\n",piece_len);
}
return 0;
}