poj 1011 Sticks(经典DFS+剪枝)

题意:给出n根小棒的长度stick[i],已知这n根小棒原本由若干根长度相同的长木棒(原棒)分解而来。求出原棒的最小可能长度。

先说先具体的实现:求出总长度sum和小棒最长的长度max,则原棒可能的长度必在max~sum之间,然后从小到大枚举max~sum之间能被sum整除的长度len,用dfs求出所有的小棒能否拼凑成这个长度,如果可以,第一个len就是答案。

下面就是关键的了,就是这道题dfs的实现和剪枝的设计:

      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,很强大。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

int s[100],vis[100];
int n,num,len;

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

int DFS(int cur,int k,int cnt)
{
    if(cnt==num) return 1;
    if(cur==len) return DFS(0,0,cnt+1);
    int pre=0;//pre用来计算重复长度的木棍(剪枝)
    int i;
    for(i=k;i<n;i++)
    {
        if(vis[i]==0&&s[i]+cur<=len&&s[i]!=pre)
        {
            pre=s[i];
            vis[i]=1;
            if(DFS(s[i]+cur,i+1,cnt))
                break;
            vis[i]=0;
            if(k==0) return 0;
        }
    }
    if(i==n) return 0;
    else return 1;
}

int main()
{
    while(cin>>n&&n!=0)
    {
        int sum=0;
        for(int i=0;i<n;i++)
        {
            cin>>s[i];
            sum+=s[i];
        }
        sort(s,s+n,cmp);
        for(len=s[0];len<=sum/2;len++)
        {
            if(sum%len==0)
            {
                num=sum/len;
                memset(vis,0,sizeof(vis));
                if(DFS(0,0,0))
                    break;
            }
        }
        if(len>sum/2) cout<<sum<<endl;
        else cout<<len<<endl;

    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Fy1999/p/9057512.html