问题描述: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; }}