POJ - 1011 Sticks(dfs+剪枝)(好题!!)
題目鏈接:點(diǎn)擊查看
題目大意:喬治拿來(lái)一組等長(zhǎng)的木棍,將他們隨機(jī)砍斷,使得每一節(jié)木棍的長(zhǎng)度都不超過(guò)50個(gè)單位長(zhǎng)度,然后他又想將這些木棍恢復(fù)成砍斷之前的狀態(tài),但他忘記了初始時(shí)有多少根木棍以及木棍的初始長(zhǎng)度了,請(qǐng)幫喬治計(jì)算一下原木棍的可能最小長(zhǎng)度
題目分析:如果只是思路的話很簡(jiǎn)單,和之前做過(guò)的那道小貓下山的題的思路相似,從小到大枚舉原始木棍的長(zhǎng)度,枚舉范圍是木棍中的最大值到所有木棍之和,然后每次都搜索一下能否完全組成,最后所有的木棍都使用完畢的話說(shuō)明該長(zhǎng)度可以構(gòu)成答案,返回即可,這個(gè)思路是沒(méi)問(wèn)題的,但如果真的實(shí)現(xiàn)后交上去發(fā)現(xiàn)會(huì)T掉,那么我們可以考慮剪枝
首先我們可以想到優(yōu)化一下枚舉順序,因?yàn)樗械哪竟鞫家玫?#xff0c;所以我們不妨從最長(zhǎng)的木棍開(kāi)始枚舉,因?yàn)檩^短的木棍比較長(zhǎng)的木棍要靈活,所以當(dāng)最長(zhǎng)的木棍在當(dāng)前無(wú)法進(jìn)行拼接時(shí),那么之后它也肯定沒(méi)法拼接上了,直接回溯即可
還有就是,針對(duì)于兩兩相鄰的木棍來(lái)說(shuō),前一個(gè)木棍已經(jīng)不能用的時(shí)候,后面一個(gè)相同長(zhǎng)度的木棍肯定也不能用,直接跳過(guò)即可
不過(guò)這兩個(gè)優(yōu)化還不夠徹底,我們還有幾個(gè)思路可以大幅度優(yōu)化效率(我是想不到)
我們?cè)谄唇釉寄竟鲿r(shí),使用的第一根木棍就拼接失敗,就可以直接回溯了,因?yàn)檫@根木棍在后續(xù)拼接時(shí)肯定也無(wú)法使用,所以沒(méi)必要繼續(xù)遞歸了(這個(gè)剪枝是這個(gè)題的核心剪枝,如果沒(méi)有這一條會(huì)直接T掉,加上這一條能以32ms的時(shí)間AC)
然后還有一個(gè)小剪枝,有點(diǎn)難想,然后重要程度比上面那個(gè)低一點(diǎn),就是當(dāng)我們?cè)谄唇釉寄竟鲿r(shí),使用當(dāng)前木棍可以將其拼接完成,但是卻返回了失敗,這時(shí)就可以直接回溯了,因?yàn)橛靡徽竟髌唇油昕隙ū葞赘〉哪竟髌唇酉嗤拈L(zhǎng)度要更優(yōu),然后加上這條剪枝,就能以16ms的時(shí)間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:拼成原木棍還需要的長(zhǎng)度 {if(cnt==n&&remain==0)//如果已經(jīng)處理完了所有的木棍,并且最后的剩余長(zhǎng)度為0,說(shuō)明滿(mǎn)足約束return true;else if(remain==0)//否則如果剩余長(zhǎng)度為0,更新成原長(zhǎng)度lenremain=len;for(int i=1;i<=n;i++){if(vis[i])//如果當(dāng)前木棍已經(jīng)用過(guò)了continue;if(a[i]>remain)//如果當(dāng)前木棍長(zhǎng)度比剩余長(zhǎng)度要長(zhǎng)continue;if(i>1&&a[i]==a[i-1]&&!vis[i-1])//如果當(dāng)前相同長(zhǎng)度的木棍已經(jīng)搜索過(guò)并且無(wú)法使用continue;vis[i]=true;if(dfs(cnt+1,remain-a[i]))//滿(mǎn)足前提條件后,進(jìn)一步搜索return true;vis[i]=false;if(remain==len)//如果第一根組成原長(zhǎng)度的木棍無(wú)法使用,直接回溯return false;if(remain==a[i])//如果當(dāng)前木棍拼接后可以正好組成原木棍的長(zhǎng)度,但是卻返回了失敗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);//優(yōu)化枚舉順序int flag=-1;for(len=a[1];len<=sum/2;len++)//枚舉到sum/2即可,當(dāng)len大于二分之sum時(shí)肯定不能整除了{(lán) 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//如果沒(méi)找到答案。就輸出sum即可,即所有的木棍只能拼成一根長(zhǎng)度為sum的木棍printf("%d\n",sum);}return 0; }?
總結(jié)
以上是生活随笔為你收集整理的POJ - 1011 Sticks(dfs+剪枝)(好题!!)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: HDU - 1584 蜘蛛牌(dfs+最
- 下一篇: POJ - 2142 The Balan