Sticks

问题描述:http://acm.pku.edu.cn/JudgeOnline/showproblem?problem_id=1011

主要想用回溯法的思想解决它。关键是要确定搜索的顺序。要提高算法的效率还要找到合适的剪枝。

// Sticks1.cpp : Defines the entry point for the console application.
// http://acm.pku.edu.cn/JudgeOnline/showproblem?problem_id=1011
// 首先将所有的小棒按从大到小的顺序排序
// 搜索策略:
// 每次用小棒组成原始棒,都要用到当前剩余的最长的小棒
// 搜索每根原始棒的时候,组成它的每个小棒的长度由大到小排列,
// 所以Search的时候有个startno,表示搜索下一个小棒的时候,从startno开始搜索
#include "stdafx.h"
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;

short len[64];
bool b[64];
short n,poslen,sticks;

//stickno为要组装的第几根原始棒,startno为组装第stickno个原始棒的时候,从第startno个小棒开始查找,
//leftlen表示组装第stickno个原始棒的时候,还缺多leftlen的长度
bool Search(short stickno, short startno, short leftlen)
{
 //搜索到最后一个原始棒,剩余的小棒的总长度必然等于原始棒的长度
 if(stickno==sticks-1) return true;

 //搜索第stickno个原始棒,如果第stickno个小棒还没使用,表明
 //第stickno个小棒不能参与组成原始棒,则整个搜索失败
 if( stickno>0 && !b[stickno-1] ) return false;

 for(int i=startno; i<n; i++)
 {
  if(!b[i])
  {
   //如果有连续的几根小棒长度相等,且前一个小棒不被使用,则当前小棒也不能使用,避免重复搜索
   if( (i>0)&&(!b[i-1]) && (len[i-1]==len[i])) continue;

   //正好组成一个原始棒
   if(len[i]==leftlen)
   {
    b[i] = true;
    //搜索下一个原始棒,如果成功则返回true,如果失败,则回溯
    if (Search(stickno+1,0,poslen)) return true;
    b[i] = false;
   }
   else if(len[i]<leftlen)
   {
    int j = n-1;
    while(b[j])
     j--;
    
    if(len[i]+len[j]<=leftlen)
    {
     b[i] = true;
     if(Search(stickno,i+1,leftlen-len[i])) return true;//此处的第二个参数设为i+1非常关键,提高了搜索速度
     b[i] = false;
    }
   }
  }
 }

 return false;
}

void main()
{
 short i,maxlen,totallen;
 cin>>n;
 while(n)
 {
  maxlen = totallen = 0;

  for(i=0; i<n; i++)
  {
   cin>>len[i];
   if(len[i]>50)
    len[i]=0;
   totallen += len[i];
   if(len[i]>maxlen)
    maxlen = len[i];
  }

  //对每小段的大小按从大到小的顺序排序
  sort(&len[0],&len[n],greater<int>());
  //清除长度为0的棒
  i=0;
  while((i<n)&&(len[i]>0)) i++;
   n = i;

  for(poslen=maxlen; poslen<totallen; poslen++)
  {
   //棒的个数不为整数则改变poslen的大小再试
   if(totallen%poslen) continue;

   //将所有小段设为未选择
   memset(b,0,n*sizeof(bool));

   //为每根长度为poslen的stick找到各个小段,这些小段的和为poslen
   sticks = totallen / poslen;
   if(Search(0,0,poslen))
    break;
  }
  cout<<poslen<<endl;

  cin>>n;
 }
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值