[題解]luogu_P1120小木棍(搜索)
好久以前抄的題解,現在重新抄題解做一下
1.對所有木棍從大到小排序,後用小的比較靈活
2.限制加入的木棍單調遞減,因為先/后用長/短木棍等價,反正就是那兩根
3.預處理出重複木棍的位置,防止重複搜索相同的木棍
4.二分查找下一根小於等於未拼木棍長度的木棍
5.因為是從小到大枚舉原木棍長度,所以第一次找到可行解就是最優的,直接停止
6.如果當前選擇木棍長度等於當前未拼木棍的長度,並且繼續搜索失敗時,就不再搜了
因為如果不用這根拼的話必然要拿更小的幾根木棍拼好當前未拼的長度,
而晚用長木棍早用短木棍只會更加不靈活,一定不會更優(最好情況下也是等價的)
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; int n,m,a[66],v[66],nxt[66],sum,cnt,len,flag; void dfs(int k,int las,int res){//k正在拼的木棍編號 //last所用上一節的編號,res當前剩餘未拼長度 int i; if(!res){//這跟拼完 if(k==m){flag=1;return;}//5.全部拼完立即退出 for(i=1;i<=cnt;i++) if(!v[i])break; v[i]=1; dfs(k+1,i,len-a[i]); v[i]=0; if(flag)return;//6. } int l=las+1,r=cnt,mid; while(l<r){//4.二分找下一根木棍 mid=(l+r)>>1; if(a[mid]<=res)r=mid; else l=mid+1; } for(int i=l;i<=cnt;i++){ if(!v[i]){ v[i]=1; dfs(k,i,res-a[i]); v[i]=0; if(flag)return; if(res==a[i] || res==len)return;//6.上面沒有return,組合失敗,這裡return i=nxt[i];//3.重複的長度不搜索 if(i==cnt)return; } } } bool cmp(int a,int b){return a>b;} int main() { scanf("%d",&n); for(int i=1,d;i<=n;i++){ scanf("%d",&d); if(d<=50)a[++cnt]=d,sum+=d; } sort(a+1,a+cnt+1,cmp);//1. nxt[cnt]=cnt; for(int i=cnt-1;i>=1;i--){ if(a[i]==a[i+1])nxt[i]=nxt[i+1]; else nxt[i]=i; } for(len=a[1];len<=sum/2;len++){ if(sum%len!=0)continue;//不能整除跳過(顯然 m=sum/len;//5. flag=0; v[1]=1; dfs(1,1,len-a[1]); v[1]=0; if(flag){ printf("%d\n",len);return 0;//5. } } printf("%d\n",sum); }