1. 程式人生 > >POJ-1011-Sticks (dfs深搜+剪枝)

POJ-1011-Sticks (dfs深搜+剪枝)

原題連結:
http://poj.org/problem?id=1011
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
題意:
有n個木棍,每一個木棍都有自己的長度(大於0的整數),這些木棍是由一些長度相同的木棍裁斷形成的,現在要求找出滿足題意的原有木棍的最小長度。
題意:
一開始,想用二分,但發現最終還是要搜尋,所以就直接列舉長度搜索了。
dfs搜尋部分,如果當前木棍可以拼接成一個長度合理的木棍的話,繼續深搜,如果不能則應該繼續搜尋所有木棍看能否組成長度合理的木棍,直到恰好可以拼成應有的木棍數。
剪枝:
如果長度總和必須是單一木棍長度的倍數,搜尋時,如果該長度不能滿足條件,則可以直接跳過該長度的木棍。
(更多細節見程式碼)
附上AC程式碼:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int n,st[65];
int sum,num,len;//sum記錄所有木棍的總長度,len記錄最小的滿足題意的長度,num記錄在某一個長度len下木棍總數
bool vis[65];
bool cmp(int a,int b)//定義排序方式
{
    return a>b;
}

bool dfs(int s,int l,int pos)//s:已組成木棍的個數,l當前木棍的長度,pos:開始拼接木棍的位置
{
    bool flag = (l == 0?true:false);
    if(s==num)return true;
    for(int i = pos + 1;i <=n;i++)
    {
        if(vis[i])continue;
        if(l + st[i]==len)//可以拼接成len的長度
        {
            vis[i] = true;
            if(dfs(s+1,0,0))//繼續深搜
            return true;
            vis[i] = false;
            return false;
        }
        else if(l + st[i]<len)//與第i個木棍拼接起來不夠len長
        {
            vis[i] = true;
            if(dfs(s,l+st[i],i))//繼續深搜
            return true;
            vis[i] = false;
            if(flag)return false;//如果當前木棍長度為0,也就是當前的這跟木棍無法組成一根長度為len的情況
            while(st[i]==st[i+1])i++;//不對於相同長度的來拼接,合理剪枝
        }
    }
    return false;
}
int main()
{
    while(scanf("%d",&n)!=EOF,n)
    {
        sum=0;
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&st[i]);
            sum+=st[i];
        }
        sort(st+1,st+n+1,cmp);//從大到小排序
        for(len=st[1];len<=sum;len++)//len所有可能的範圍
        {
            if(sum%len==0)//總長度sum必須是len的倍數
            {
                num=sum/len;
                memset(vis,false,sizeof(vis));//重置
                if(dfs(1,0,0))//滿足題意,輸出最小值
                {
                    printf("%d\n",len);
                    break;
                }
            }
        }
    }
    return 0;
}

歡迎評論!