POJ - 1011 Sticks(dfs+剪枝)(好题!!)
題目鏈接:點擊查看
題目大意:喬治拿來一組等長的木棍,將他們隨機砍斷,使得每一節木棍的長度都不超過50個單位長度,然后他又想將這些木棍恢復成砍斷之前的狀態,但他忘記了初始時有多少根木棍以及木棍的初始長度了,請幫喬治計算一下原木棍的可能最小長度
題目分析:如果只是思路的話很簡單,和之前做過的那道小貓下山的題的思路相似,從小到大枚舉原始木棍的長度,枚舉范圍是木棍中的最大值到所有木棍之和,然后每次都搜索一下能否完全組成,最后所有的木棍都使用完畢的話說明該長度可以構成答案,返回即可,這個思路是沒問題的,但如果真的實現后交上去發現會T掉,那么我們可以考慮剪枝
首先我們可以想到優化一下枚舉順序,因為所有的木棍都要用到,所以我們不妨從最長的木棍開始枚舉,因為較短的木棍比較長的木棍要靈活,所以當最長的木棍在當前無法進行拼接時,那么之后它也肯定沒法拼接上了,直接回溯即可
還有就是,針對于兩兩相鄰的木棍來說,前一個木棍已經不能用的時候,后面一個相同長度的木棍肯定也不能用,直接跳過即可
不過這兩個優化還不夠徹底,我們還有幾個思路可以大幅度優化效率(我是想不到)
我們在拼接原始木棍時,使用的第一根木棍就拼接失敗,就可以直接回溯了,因為這根木棍在后續拼接時肯定也無法使用,所以沒必要繼續遞歸了(這個剪枝是這個題的核心剪枝,如果沒有這一條會直接T掉,加上這一條能以32ms的時間AC)
然后還有一個小剪枝,有點難想,然后重要程度比上面那個低一點,就是當我們在拼接原始木棍時,使用當前木棍可以將其拼接完成,但是卻返回了失敗,這時就可以直接回溯了,因為用一整根木棍拼接完肯定比幾根小的木棍拼接相同的長度要更優,然后加上這條剪枝,就能以16ms的時間AC了
上代碼吧:
#include<iostream> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=70;int n,len,sum;int a[N];bool vis[N];bool cmp(int a,int b) {return a>b; }bool dfs(int cnt,int remain)//cnt:處理到了第cnt根木棍 remain:拼成原木棍還需要的長度 {if(cnt==n&&remain==0)//如果已經處理完了所有的木棍,并且最后的剩余長度為0,說明滿足約束return true;else if(remain==0)//否則如果剩余長度為0,更新成原長度lenremain=len;for(int i=1;i<=n;i++){if(vis[i])//如果當前木棍已經用過了continue;if(a[i]>remain)//如果當前木棍長度比剩余長度要長continue;if(i>1&&a[i]==a[i-1]&&!vis[i-1])//如果當前相同長度的木棍已經搜索過并且無法使用continue;vis[i]=true;if(dfs(cnt+1,remain-a[i]))//滿足前提條件后,進一步搜索return true;vis[i]=false;if(remain==len)//如果第一根組成原長度的木棍無法使用,直接回溯return false;if(remain==a[i])//如果當前木棍拼接后可以正好組成原木棍的長度,但是卻返回了失敗return false;}return false;//如果所有情況都失敗,就只能返回失敗了 }int main() { // freopen("input.txt","r",stdin);while(scanf("%d",&n)!=EOF&&n){sum=0;for(int i=1;i<=n;i++){scanf("%d",a+i);sum+=a[i];}sort(a+1,a+1+n,cmp);//優化枚舉順序int flag=-1;for(len=a[1];len<=sum/2;len++)//枚舉到sum/2即可,當len大于二分之sum時肯定不能整除了{ if(sum%len)continue;memset(vis,false,sizeof(vis));if(dfs(0,len))//如果找到答案更新flag并立即退出{flag=len;break;}}if(flag!=-1)//如果找到答案,輸出答案printf("%d\n",flag);else//如果沒找到答案。就輸出sum即可,即所有的木棍只能拼成一根長度為sum的木棍printf("%d\n",sum);}return 0; }?
總結
以上是生活随笔為你收集整理的POJ - 1011 Sticks(dfs+剪枝)(好题!!)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDU - 1584 蜘蛛牌(dfs+最
- 下一篇: POJ - 2142 The Balan