1. 程式人生 > >洛谷 P1120 小木棍 題解

洛谷 P1120 小木棍 題解

這就是一道明顯的爆搜題(人家洛谷也說了,,,),只是需要幾個小小的優化,接下來為大家介紹一下:

1、將木棍先排個序(用處後面會講),記住 長度>50 的要去掉

2、因為每一根的長度都不大於50,所以可以用桶裝,既省空間也省時間

3、……先上程式碼吧,要不然直接講比較抽象(其實是我口才不好)……

————————————(華麗的分割線)————————————————

#include <cstdio>
#include <cstdlib>
#include <cstring>


int n=0,m;
int a[110];//桶
int p=0;//木棍總長度
int max=0,min=51;//最長的木棍的長度以及最短的木棍的長度
int ans;//記錄當前成功拼接了幾根木棍,在dfs中用到


void dfs(int x,int y,int z)//x表示每根木棍的期望長度,y表示現在拼成的長度,z表示上一次使用的木棍的長度 
{
    if(ans*x==p)//假如拼接成功 
    {
        printf("%d",x);
        exit(0);//直接結束,此時一定是最優解,因為他要的是最小的解
   //而我們是從小到大列舉的,第一個解必然是最優解 
    }
    for(int i=z;i>=min;i--)//因為較長的木棍更難拼,所以從長的先開始
    {
        if(a[i]&&y+i<=x)//假如還有長度為i的木棍以及拼接起來不會超過期望長度的話
        {
            y+=i;
            if(y==x)ans++,y=0;
            a[i]--;
            if(y==0)dfs(x,y,max);//如果拼接成功,則z要從max開始
            else dfs(x,y,i);
            a[i]++;
            if(ans>0&&y==0)y=x-i,ans--;
            else y-=i;
            if(y==0)break;//最重要的優化1:見下文
            if(y+i==x)break;//最重要的優化2:同上
        }
    }
}


int main()
{
    scanf("%d",&m);
    memset(a,0,sizeof(a));//一定要記得初始化!!!
    for(int i=1;i<=m;i++)
    {
        int x;
        scanf("%d",&x);
        if(x<=50)//過濾掉超過50的
        {
            a[x]++,p+=x;
            max=max<x?x:max;
            min=min>x?x:min;
        }
    }
    for(int i=max;i<=p/2;i++)//列舉原本木棍的長度,用dfs嘗試是否能拼接成功 
//由於原本木棍的長度不可能比最長的木棍長度要短,所以從max開始 
//以及原本木棍的長度一定能整除總長度(商為1的情況下面會處理),所以假如i>p/2的時候
//i一定不能整除p,所以沒必要dfs,直接跳過 
    {
        if(p%i==0)//如果不能整除則跳過(不能整除則說明會有餘數,也就是多餘的木棍) 
        {
            ans=0;//一定要記得初始化!!! 
            dfs(i,0,max); 
        }
    }
    printf("%d",p);//假如上面未成功,就只能把全部拼在一起了
}

最重要的優化1:首先要思考,什麼時候y==0,當然是剛開始拼接新的一根木棍時,而這條語句在dfs下面,也就是說拼接沒有成功,才會到達這裡,也就剩下那麼些木棍,如果既然拼接都不成功了,那麼也就沒有必要嘗試其他的了,於是break

優化2:上面y==0 break之後,就會到達上一根木棍的最後拼接的時候,那前面都知道是不可以的了,那也沒有必要繼續了,於是也break

以上就是我的想法,如果有人有更好的優化的話,歡迎評論