思路:對可行長度進行枚舉,通過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;
}