1. 程式人生 > >POJ 1011 Sticks中的玄學問題

POJ 1011 Sticks中的玄學問題

題目如下:

Description

George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were originally. Please help him and design a program which computes the smallest possible original length of those sticks. All lengths expressed in units are integers greater than zero.

Input

The input contains blocks of 2 lines. The first line contains the number of sticks parts after cutting, there are at most 64 sticks. The second line contains the lengths of those parts separated by the space. The last line of the file contains zero.

Output

The output should contains the smallest possible length of original sticks, one per line.

Sample Input

9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0

Sample Output

6
5

這道題的思路是從最小約數開始找起,每找到一個約數後,用深搜的方法判斷小棒們是否能夠組合成這個約數的長度。這道題的關鍵在於深度搜索的剪枝,不剪枝很容易出現超時錯誤。

  1.      以一個小棒為開頭,用dfs看看能否把這個小棒拼湊成len長,如果可以,用vis[i]記錄下用過的小棒,然後繼續以另外一個小棒為開頭,以此類推。
  2.       小棒的長度從大到小排序,這個就不解釋了。
  3.       如果當前最長的小棒不能拼成len長,那麼就返回前一步,更改前一步的最長小棒的組合情況(這裡不能是全部退出),不用再繼續搜尋下去了。
  4.       最重要的,就是比如說17,9,9,9,9,8,8,5,2……如果當前最長小棒為17,它與第一個9組合之後dfs發現不能拼成len,那麼17就不用和後面所有的9組合了,而直接和8開始組合。這個剪枝直接從TLE到16MS,很強大。

AC程式碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>

using namespace std;
int number,stick[65];
bool used[65];
bool result = false;
int length;

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

int find(int k,int culen)
{
    //cout<<number<<" %%% "<<k<<endl;
    while((used[k] || stick[k]+culen>length) && k<number ){k++;}
    return k;
}

void dfs(int culen, int p, int usen)
{
    //cout<<"culen: "<<culen<<" k: "<<k<<" usen: "<<usen<<endl;
    int k;
    if(result)return;
    //if(k>=number)return;
    //cout<<"k="<<k<<" and stick is"<<stick[k]<<endl;
    if(culen<length)
    {
        //while(stick[k]+culen>length && k<number){k = find(k+1);}
        if(culen!=0){k = find(p,culen);if(k>=number)return;}
        else {k = 0;while(used[k])k++;}

        used[k]=true;
        dfs(stick[k]+culen,k+1,usen+1);
        used[k]=false;
        if(culen==0)return;

        int ked = k;
        while(k<number){
            k = find(k+1,culen);
            if(k>=number)break;
            if(stick[k-1]==stick[k] && !used[k-1])continue;
            used[k] = true;
            dfs(stick[k]+culen,k+1,usen+1);
            ked = k;
            used[k]=false;
        }
        return;
    }

        if(culen==length){
        //cout<<"\tsuccess!"<<endl;
        if(usen == number){
            result = true;
            //cout<<"\tsuccess finally!"<<endl;
            return;
        }
        dfs(0,0,usen);
    }
    return;
}

int main()
{
    //int number;
    while(scanf("%d",&number) && number!=0){
        int sum=0;
        result = false;
        for(int i=0;i<number;i++)
        {
            scanf("%d",&stick[i]);
            sum+=stick[i];
            //if(maxstick<stick[i])maxstick=stick[i];
        }
        //int lo = 1;
        sort(stick,stick+number,cmp);
        //result = false;
        for(length=stick[0]; length<sum;length++)
        {
            if(sum%length==0){
                //length = i;
                memset(used, 0, sizeof(used));
                dfs(0,0,0);
                //cout<<"length="<<length<<endl;
                if(result)break;
            }

        }
        printf("%d\n",length);

    }
    return 0;
}

那麼問題就來了, 上面的程式碼中主函式中的length變數是一個全域性變數,在for迴圈裡面它直接充當了控制變數,但是如果將這個for迴圈改成下面的樣子就會出現WA。。

for(int i=stick[0]; i<sum;i++)
        {
            if(sum%i==0){
                length = i;
                memset(used, 0, sizeof(used));
                dfs(0,0,0);
                //cout<<"length="<<length<<endl;
                if(result)break;
            }

        }

真的特別迷...