1. 程式人生 > >小木棍【資料加強版】

小木棍【資料加強版】

這道題我認為還是有些難度的,首先你得知道怎麼搜,思路是這樣的:

我們可以搜尋當前正在拼的是第幾根木棍,當前木棍還有多少長度沒有拼,以及上一個用的木棍的編號(這個是為了後面的優化用的,如果不加的話可以正常地進行,但會超時)

然後這道題的真正難點在於它的優化,我們把用到的優化依次列舉如下:

優化零:下面的很多優化都是基於木根長度是一個降序的序列的基礎上進行的,所以要先進行排序。

優化一:首先我們可以按照從大到小選取木棍的思路來進行搜尋,因為這樣搜起來更為靈活,自己可以腦補一下搜尋的過程:若我們先放大的,最後放小的,會感覺小的能夠用的特別合理靈活;而若是先放小的,則到了後面大的木根似乎就沒有那麼靈活了,意會一下吧。(如果運用能力強的人或許能夠講這種思想應用到其他搜尋題目中吧)

優化二:一旦找到答案就立刻返回,無需任何的猶豫,這樣能夠最大限度的節省時間。*(可以用到其他的搜尋題中)

優化三:建一個next陣列,如果我們在搜某個長度的木棍失敗後,那麼和它相同長度的也就不用在搜了,可以直接跳到不同長度的木棍上,這個樣子雖然看似沒多大用,但可以想像去搜一個長度就意味著會拓展出許多的新的搜尋分支,這樣也是十分耗時的。(對於一個狀態可以拓展出許多分支的題目可以運用)

優化四:由於我們事先規定的是從大向小的搜,所以我們這裡可以記錄last,作為上次用的木棍的編號,本次搜尋中只需二分小於當前需要拼的長度的木棍的最大值,然後列舉要用的木棍時直接從這個範圍內開始就可以了。(某些題目可以應用,但或許並不常見)

優化五:如果當前一個木根的長度等於未拼長度或者等於我們一開始列舉的那個最小可能長度,且此時對於它進行的搜尋失敗了,那麼可以直接返回了,因為此時我們所定的這個最小可能長度一定是不可行的。

優化六:見程式碼註釋*(可以用到其它搜尋中)

優化七:最小可能長度的範圍一定是在當前木棍最長長度到木棍總長度之間。其實可以只列舉到總長度的一半,因為若到了此時還沒有找到解得話,那麼答案就一定是總長度了。*(可以用到其它搜尋之中)

優化八:失敗之後及時還原vis,少用memset,用多了特別慢。*(可以用到其它搜尋中)

code:

// luogu-judger-enable-o2
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;

const int N=70;
int n, cnt, a[N];
int now, m, next[N];
bool used[N];

bool cmp(int a, int b) {
    return a>b;
}

bool dfs(int cur, int rest, int last) {
    if (rest==0) {
        if (cur==m) return true;
        int i;
        for (i=1; i<=cnt; i++)
            if (!used[i]) break;
        used[i]=true;
        if (dfs(cur+1, now-a[i], i)) return true;
        used[i]=false;
        return false;
    }
    int l=last, r=cnt;
    while (r-l>1) {
        int mid=(l+r)>>1;
        if (a[mid]<=rest) r=mid;
        else l=mid;
    }
    for (int i=r; i<=cnt; i++) {
        if (used[i]) continue;
        used[i]=true;
        if (dfs(cur, rest-a[i], i)) return true;
        used[i]=false;
        if (a[i]==rest || a[i]==now) return false;
        i=next[i];
        if (i==cnt) break;
    }
    return false;
}

int main() {
    int x, sum=0;
    scanf("%d", &n);
    for (int i=1; i<=n; i++) {
        scanf("%d", &x);
        if (x>50) continue;
        a[++cnt]=x;
        sum+=x;
    }
    sort(a+1, a+cnt+1, cmp);
    next[cnt]=cnt;
    for (int i=cnt-1; i>=1; i--)
        if (a[i]==a[i+1]) next[i]=next[i+1];
        else next[i]=i;
    for (int i=a[1]; i<=(sum>>1); i++) {
        if (sum%i!=0) continue;
        now=i;
        m=sum/i;
        used[1]=true;
        if (dfs(1, now-a[1], 1)) {//優化六,直接前進一步來進行搜尋,而不要dfs(1, now, 0),在這道題目中效果特別明顯
            printf("%d", i);
            return 0;
        }
        used[1]=false;
    }
    printf("%d", sum);
    return 0;
}