1. 程式人生 > 實用技巧 >POJ1011小木棍題解

POJ1011小木棍題解

POJ1011小木棍題解

首先我認為題目中的喬治就是個傻X——哪有切斷又接回去的((

轉回正題。

題目傳送門:POJ1011

題意簡介

有個智力低下(盲猜此人乃近親通婚之產物)的人名叫喬治,他有一天突然得到了一組長度相同的木棍(物業不知道有多少個),然後他的大腦皮層肌肉死亡痙攣,突然決定將這些木棍隨機切碎,長度不一。好嘛,這個人的大腦皮層肌肉突然復活,又痙攣了一下,很快啊,很快,他就決定將這些木棍拼接回去。現在給你切碎後每個小木棍的長度,問你最開始那組長度一致的木棍,每根木棍的長度至少是多少。儘管你覺得他不講武德,但你還是要做這道題(耗子尾汁)。

題意分析

首先我們發現可以列舉答案\(len\),從小到大列舉,符合條件就退出。

如何判斷他是否滿足條件呢?我們可以使用搜索來解決這道題。搜尋中要維護\(3\)個狀態,變數\(stick\)表示當前正在搞第\(stick\)根原始木棍,\(cab\)表示當前正在湊的這根原始木棍的長度是多少(換句話說\(len-cab\)就是我們要成全這根木棍需要湊出的長度),\(lst\)表示上次湊時使用的小木棍。

然後你就發現你\(TLE\)了。

如果你是一個老\(OIer\),你肯定知道這道題是道剪枝模板題。怎麼剪枝呢?本題有以下\(5\)種剪枝。

\((1).\)優化搜尋順序:顯然先嚐試較長的小木棍比隨機嘗試要優,所以我們要在搜尋前先排序

\((2).\)排除等效冗餘:限制先後加入一根切碎後的木棒的長度是遞減的,因為先加入\(x\)

後加入\(y\)和先加入\(y\)後加入\(x\)是一樣的,只需其中一種即可

\((3).\)排除等效冗餘:對於當前原始木棍,記錄其最近一次嘗試的新木棍,如果分支失敗回溯,那麼不必繼續嘗試向該原始木棍中新增其他等長的新木棍,因為也必定失敗

\((4).\)排除等效冗餘:如果在當前原始木棍中新增一根新木棍並恰好拼接完整,並且接下來的兒子分支遞迴失敗,那麼直接判定當前分支失敗,原理類似於遠遠地望去是一個死衚衕就立刻回頭。

\((5).\)如果在當前原始木棍中嘗試接入的第一根木棍就失敗,那麼直接判定分支失敗並立即回溯。因為在這之前這根原始木棍是空的,後面的原始木棍必定也是空的,這跟空的接不了後面的肯定也接不了

AC程式碼:POJ1011 AC 16ms/640kb

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=110;
int a[N],v[N],n,len,cnt;
int sum,val;
int read()
{
    int x=0,f=1;
    char c=getchar();
    while (c<'0'||c>'9')
    {
        if(c=='-') f=-1;
        c=getchar();
    }
    while (c>='0'&&c<='9')
    {
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x*f;
}
bool DFS(int stick,int cab,int lst)
{
    if(stick>cnt) return true;
    if(len==cab) return DFS(stick+1,0,1);
    int fail=0;
    for(int i=lst;i<=n;i++)
    {
        if(!v[i] && cab+a[i]<=len && fail!=a[i])
        {
            v[i]=1;
            if(DFS(stick,cab+a[i],i+1)) return true;
            fail=a[i];
            v[i]=0;
            if(!cab || cab+a[i]==len) return false;
        }
    }
    return false;
}
int main()
{
    while (n=read())
    {
        if(!n) break;
        sum=0,val=0;
        for(int i=1;i<=n;i++)
        {
            a[i]=read();
            sum+=a[i];
            val=max(val,a[i]);
        }
        sort(a+1,a+n+1);
        reverse(a+1,a+n+1);
        for(len=val;len<=sum;len++)
        {
            if(sum%len) continue;
            cnt=sum/len;
            memset(v,0,sizeof(v));
            if(DFS(1,0,1)) break;
        }
        cout<<len<<endl;
    }
    return 0;
}