POJ 1011

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

思路:dfs+剪枝。蛮经典的题目,重点在于dfs剪枝的设计。先说先具体的实现:求出总长度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开始组合。

#include<iostream>
#include<cstring>
using namespace std;
int stick[65],n,sum,len;
bool vis[65],flag;
void init()
{
     int i,j;
        memset(stick,0,sizeof(stick));
  
        for(i=0,sum=0;i<n;i++)
  {
   cin>>stick[i];
   sum+=stick[i];                  //所有木棍长度和
  }
  for(i=0;i<n;i++)               //将木棍用冒泡排序,因为最多64组数据,故快排和冒泡时间性能相差很很小
     for(j=i+1;j<n;j++)
     {
     if(stick[i]<stick[j])
     {
      int  mid=stick[i];
      stick[i]=stick[j];
      stick[j]=mid;
     }
     }
  
  flag=false;
}
void dfs(int dep, int now_len, int u)    // dep为当前已被用过的小棒数,u为当前要处理的小棒。
{                                            
    if(flag) return;
    if(now_len == 0){                    //  当前长度为0,寻找下一个当前最长小棒。
        int k = 0;
        while(vis[k]) k ++;              //  寻找第一个当前最长小棒。
        vis[k] = true;
        dfs(dep + 1, stick[k], k + 1);
        vis[k] = false;
        return;
    }
    if(now_len == len){                  //  当前长度为len,即又拼凑成了一根原棒。
        if(dep == n) flag = true;        //  完成的标志:所有的n根小棒都有拼到了。
        else dfs(dep, 0, 0);
        return;
    }
    for(int i = u; i < n; i ++)
        if(!vis[i] && now_len + stick[i] <= len){
            if(!vis[i-1] && stick[i] == stick[i-1]) continue;      //  不重复搜索:最重要的剪枝。
            vis[i] = true;
            dfs(dep + 1, now_len + stick[i], i + 1);
            vis[i] = false;
        }
}


int main()
{
 
 while(cin>>n)
 {
  if(!n)  break;
        init();
  for(len=stick[0];len<=sum;len++)                
  {
   if(sum%len==0)
   {
    memset(vis,false,sizeof(vis));
    dfs(0,0,0);
    if(flag)  break;
   }
  }
  cout<<len<<endl;
 }
 return 0;
}

 

你可能感兴趣的:(POJ 1011)