1. 程式人生 > >洛谷P1120 小木棍

洛谷P1120 小木棍

AD ive 傳送門 cnblogs 剪枝 %d 同時 開始 格式

  題目傳送門

題目描述
喬治有一些同樣長的小木棍,他把這些木棍隨意砍成幾段,直到每段的長都不超過50。

現在,他想把小木棍拼接成原來的樣子,但是卻忘記了自己開始時有多少根木棍和它們的長度。

給出每段小木棍的長度,編程幫他找出原始木棍的最小可能長度。

輸入輸出格式
輸入格式:
輸入文件共有二行。

第一行為一個單獨的整數N表示砍過以後的小木棍的總數,其中N≤65

(管理員註:要把超過50的長度自覺過濾掉,坑了很多人了!)

第二行為N個用空個隔開的正整數,表示N根小木棍的長度。

輸出格式:
輸出文件僅一行,表示要求的原始木棍的最小可能長度

輸入輸出樣例
輸入樣例#1:
9
5 2 1 5 2 1 5 2 1


輸出樣例#1:
6
說明
2017/08/05

數據時限修改:

-#17 #20 #22 #27 四組數據時限500ms

-#21 #24 #28 #29 #30五組數據時限1000ms

其他時限改為200ms(請放心食用)


  分析:CL大佬講課的時候講到了這題,於是就來做一下。但是講的時候沒聽,於是做的時候遇到了瓶頸,還是參考了five20大佬的博客。確定了搜索的思路,那麽要考慮的就是如何剪枝,這題真的是一道練習搜索剪枝的絕(e)世(xin)好(gui)題。總共需要用到一下幾種剪枝:

  1,搜索範圍:很顯然,答案肯定是在[maxn,sum]內,maxn為給定木棍的最大長度,sum為給定木棍長度之和,同時正確答案一定能被sum整除,所以搜的時候範圍就確定在maxn到sum/2之間能把sum整除的所有整數。如果在該範圍內均不滿足,那麽答案就是sum。

  2,搜索順序:由上面得出的範圍可知,從大到小搜索更優,而且根據題目可知,木棍長度<=50,那麽也不需要快排了,直接桶排,記錄每種長度的木棍的數目即可。

  3,如果搜索過程中已經使用過長度為x的木棍,那麽下一層搜索直接從x開始從大到小搜,因為顯然此刻使用了x那麽比x長的都已經不可行了。

  4,如果某次搜索時如果當前長度為0或當前長度加選擇木棍的長度等於目標長度,那麽搜完以後直接break,因為是從大到小枚舉,因此再小的木棍就不能達到目標長度了。(這個優化可能這麽說並不太好理解,結合代碼應該就好懂了)

  Code:

  

//It is made by HolseLee on 19th Apr 2018
//Luogu.org P1120 #include<bits/stdc++.h> using namespace std; const int N=101; int n,num[N],sum,cnt; int maxn=0,minn=N; inline void dfs(int tar,int now,int len,int m) { if(!tar){printf("%d",len);exit(0);} if(now==len){ dfs(tar-1,0,len,maxn); return;} for(int i=m;i>=minn;i--){ if(now+i<=len&&num[i]){ num[i]--; dfs(tar,now+i,len,i); num[i]++; if(!now||now+i==len) break;} } } int main() { ios::sync_with_stdio(false); cin>>n;int x; for(int i=1;i<=n;i++){ cin>>x; if(x<=50){ num[x]++;cnt++; maxn=max(maxn,x); minn=min(minn,x); sum+=x;} } for(int i=maxn;i<=sum/2;i++){ if(sum%i==0)dfs(sum/i,0,i,maxn); } printf("%d",sum); return 0; }

洛谷P1120 小木棍