洛谷P1120 小木棍(公升級版)

2022-04-05 22:35:12 字數 2408 閱讀 6562

傳送門啦

一道經典的搜尋剪枝題,不廢話,步入正題。

一、輸入時手動過濾不合法的情況

二、很明顯我們要列舉把哪些棍子拼接成原來的長棍,而原始長度(原來的長棍的長度)都相等,因此我們可以在 $ dfs $ 外圍列舉拼接後的每根長棍的長度。那列舉什麼範圍呢?

其長度至少是最長的一根木棍,此時最長的這根木棍恰好單獨組成原來的長棍。否則這根最長的木棍就無法自己或與其它木棍組成原來的長棍。其長度至多是所有木棍的長度之和,此時所有的木棍拼在一起恰好成為一根原來的長棍。

$ dfs $ 一定會超時。所以我們考慮到當原始長度 不能被 所有木棍的長度之和 整除的話,這些木棍是拼不出整數根的(如果都拼成列舉的原來長棍的長度)。

原始長度列舉到 所有木棍的長度之和/2 即可,因為此時所有木棍有可能拼成2根木棍,原始長度再大的話就只能是所有木棍拼成1根了,所以如果最後我們沒有得出乙個合法的方案,就直接把 $ sum $ 輸出,即原來木棍是一根長度為 $ sum $ 的木棍。

三、下面就該考慮怎麼搜尋了。

dfs(int k , int last , int rest)分別表示正在拼第幾根原來的長棍,使用的上一根木棍的編號,當前在拼的長棍還有多少長度未拼

光是這些肯定是不夠的。。

四、各種優化。

1.一根長木棍肯定比幾根短木棍拼成同樣長度的用處小,可以說是短木棍靈活(??),所以對輸入的所有木棍按長度從大到小排序,這樣短木棍可以更加靈活地接在原始木棍上。

2.根據1,將輸入的木棍排好序後,當用木棍 $ i $ 拼合原始長棍時,從第 $ i + 1 $ 根木棍開始往後搜。在分析二中我們也討論了原始木棍的範圍了,大於等於最長的一根木棍。

3.當 $ dfs $ 返回拼接失敗,需要更換當前使用的木棍時,不要再用與當前木棍的長度相同的木棍,因為當前木棍用了不行。可以用 $ next[ ] $ 預處理出了排序後每根木棍後面的最後一根與這根木棍長度相等的木棍,它的下一根木棍就是第一根長度不相等的木棍了。

4.只找木棍長度不大於未拼長度rest的所有木棍。可以根據木棍長度的單調性來二分找出第乙個木棍長度不大於未拼長度 $ rest $ 。它後面的木棍一定都滿足這個條件。

5.由於是從小到大列舉 原始長度,因此第一次發現的答案就是最小長度。dfs中只要發現所有的木棍都湊成了若干根原長度的長棍(容易發現 湊出長棍的根數=所有木棍的長度之和/原始長度),立刻一層層退出dfs,不用滯留,退到dfs外後直接輸出原始長度並結束程式。

最後乙個優化,也是最難最不好想最不好理解的乙個。

其實如果不加最後這乙個優化可以得到78分,考場上也是乙個比較可觀的分數。

6.還有乙個難想卻特別特別重要的優化:如果當前長棍剩餘的未拼長度等於當前木棍的長度或原始長度,繼續拼下去時卻失敗了,就直接回溯並改之前拼的木棍。有些人不太明白這個優化,這裡簡單說一下:

當前長棍剩餘的未拼長度等於當前木棍的長度時,當前木棍明顯只能自組一根長棍,但繼續拼下去卻失敗,說明這根木棍不能自組?!這根木棍不自組就沒法用上了,所以不用搜更短的木棍了,直接回溯,改之前的木棍;

當前長棍剩餘的未拼長度等於原始長度時,說明這根原來的長棍還一點沒拼,現在正在放入一根木棍。很明顯,這根木棍還沒有跟其它棍子拼接,如果現在拼下去能成功話,它肯定是能用上的,即自組或與其它還沒用的木棍拼接。但繼續拼下去卻失敗,說明現在這根木棍不能用上,無法完成拼接,所以直接回溯,改之前的木棍。

#include #include #include #include using namespace std;

inline int read()

while(ch >= '0' && ch <= '9')

return x * f;

}int n,x,a[70],tot;

int sum,m,len,next[70];

bool ok,used[70];

bool cmp(int a,int b)

void dfs(int k,int last,int rest)

for(i=1;i<=tot;i++)

if(!used[i]) break;

used[i] = 1;

dfs(k + 1 , i , len - a[i]);

used[i] = 0;

if(ok) return;

} int l = last + 1 , r = tot , mid;

while(l < r)

for(i=l;i<=tot;i++)

if(!used[i])

}int main()

} sort(a + 1 , a + 1 + tot , cmp);//優化1

next[tot] = tot;

for(int i=tot-1;i>0;i--)

for(len=a[1];len<=sum/2;len++)

} printf("%d\n",sum);

return 0;

}

洛谷 P1120 小木棍 資料加強版

題目 小木棍 思路 搜尋 剪枝。外層迭代加深,列舉最小長度,用dfs判斷。dfs維護3個變數x,y,lst,即用了x根木棍,當前拼到了y,上一根木棍的長度為lst。然後列舉拼接的木棍就好。剪枝一 從大到小排序。剪枝二 如果y不為0,那麼列舉的木棍長不要大於lst,不然就會重複搜尋。剪枝三 如果y 0...

洛谷 P1120 小木棍 資料加強版

喬治有一些同樣長的小木棍,他把這些木棍隨意砍成幾段,直到每段的長都不超過5050。現在,他想把小木棍拼接成原來的樣子,但是卻忘記了自己開始時有多少根木棍和它們的長度。給出每段小木棍的長度,程式設計幫他找出原始木棍的最小可能長度。搜尋加剪枝 剪枝 1 使用桶排,因為長度不超過50。2 列舉的長度應該能...

洛谷P1120 小木棍 資料加強版

喬治有一些同樣長的小木棍,他把這些木棍隨意砍成幾段,直到每段的長都不超過50。現在,他想把小木棍拼接成原來的樣子,但是卻忘記了自己開始時有多少根木棍和它們的長度。給出每段小木棍的長度,程式設計幫他找出原始木棍的最小可能長度。共二行。第一行為乙個單獨的整數n表示砍過以後的小木棍的總數,其中n 65 管...