题目大意是说,有值为1到6的六种珠子,判断是否可以根据值的总和,将珠子分为值相等的两半。
可以采用回溯法,采用贪心策略,在初始时从值最大的珠子和这个珠子最大的数目来回溯,但这样仍然会超时。
关键注意到一个珠子可能有20000个,这是导致超时的关键,所以要在这个地方剪枝。
假设6的数目足够多,那如果在回溯中,在解中6个5或3个4或2个3或6个1,都可以被6表示,即之前解已经是求出来的。即设6的个数为count,那么sum/2-6*count > (5* 5+2*4+1*3+2*2+5*1),即不用再回溯,直接break。类似5,4,3,2,1..都是这样。
那么如果6的数目不够呢?也会发生sum/2-6*count > (5* 5+2*4+1*3+2*2+5*1)。即如果满足这个不等式,则肯定在值5,4,3,2,1的某个肯定能至少表示1个6,即和前面一个解还是相同的,可以break。
具体看实现:
#include<iostream>
using namespace std;
unsigned short flag[7];
unsigned short cut[6] = {0,1,6,14,40,45};
bool divide = false;
void recurve(int sum,int begin){
if(divide || sum <0 || begin <1)
return;
if(sum == 0){
divide = true;
return;
}
//剪
while(flag[begin] == 0){
--begin;
}
int value = begin;
for(int i = flag[begin];i>=0 && !divide;--i){
int rest = sum-i*value;
recurve(rest,begin-1);
rest += value;
//强剪枝
if(rest > cut[begin-1])
break;
}
}
int main()
{
int sum;
int times =1;
while(true){
memset(flag,0,sizeof(flag));
sum = 0;
for(int i =1;i<7;++i){
cin>>flag[i];
sum += flag[i]*i;
}
if(sum == 0)
break;
cout<<"Collection #"<<times<<":"<<endl;
if(sum%2 == 0){
recurve(sum/2,6);
if(divide)
cout<<"Can be divided."<<endl;
else
cout<<"Can't be divided."<<endl;
}
else
cout<<"Can't be divided."<<endl;
cout<<endl;
++times;
divide = false;
}
return 0;
}