題解0007:小木棍(P1120)
阿新 • • 發佈:2022-03-25
(錯誤記錄)
題目連結:https://www.luogu.com.cn/problem/P1120
題目描述:幾根同樣長的木棍,小冥把它們隨意砍成了n段;
然後他又吃飽了撐的想把木棍拼上;
但是這個小冥是小年痴呆,他忘了他原來是咋切的了;
現在要寫一段程式,輸入n和切斷的每段木棍的長,輸出原始木棍的最小可能長度。
題目思路:搜尋剪枝,這道題搜尋其實很好說,直接從小到大列舉長度,然後遞迴判斷可行性;
難點還是在剪枝上,如果不用剪枝的話只能得9分左右,這道題要用到7個剪枝;
具體的詳見程式碼;
程式碼:
#include<bits/stdc++.h>
using namespace std;
int n,arr[66]={0},ans,brr[66]={0},we,wer;//arr存木棍長度,brr存木棍是否被用過
bool cmp(int a,int b){//將所有木棍從大到小排序
return a>b;
}
bool dfs(int a,int b,int c){//a是假定小木棍的剩餘長度,b是迴圈初始值,c是木棍根數
if(c==wer){//木棍根數對上了
return 1;
}
if(a==0){//一根木棍拼完
if(dfs(we,1,c+1)){//根數+1
return 1;
}
}
for(int i=b;i<=n;++i){
//剪枝3:下次迴圈的初始值完全可以接上上次迴圈的結束值
if(arr[i]<=a&&brr[i]!=1){//木棍沒被用過&&長度沒超
brr[i]=1;//標記
if(dfs(a-arr[i],i+1,c)){//-去長度
return 1;
}
brr[i]=0;//回溯
if(a==arr[i]||a==we){
//剪枝4:如果這個木棍長度與剩餘長度一樣,這已經是最優解了,還不行,那直接返
//剪枝5:如果剩餘長度與假定木棍長度一樣,和剪枝4一樣,直接返
return 0;//如果寫break可能過不了
}
while(arr[i+1]==arr[i]){
//剪枝6:因為排了序,所以有很多相等的數在一起,遇到相等的數直接跳
i++;
}
}
}
return 0;
}
int main(){
cin>>n;
for(int i=1;i<=n;++i){
cin>>arr[i];
ans+=arr[i];
}
sort(arr+1,arr+n+1,cmp);
for(int i=arr[1];i<=ans/2;++i){//這個數從最小木棍長度開始列舉
//剪枝1:如果列舉到所有木棍長度+起來/2還沒有找到,那結果就一定是所有數加起來了,往下搜沒意義了
if(ans%i==0){
//剪枝2:只有能被總長度整除的數才有可能是結果,不滿足條件的直接扔掉
we=i;
wer=ans/we;
if(dfs(we,1,0)){//遞迴判斷可行性
cout<<we;
return 0;//可以直接結束
}
}
}
cout<<ans;
return 0;
}