题意不说了,有中文。
搜索。高难度剪枝。
其实这道题在POJ上的数据太渣。水水的剪枝就可以过。但是UVA的那道一样的题目不行。。推荐一个好的题解。
这道题目一开始我只想到3个剪枝方法。然后百度到了另外3个。
- 首先确定答案的范围,答案必然满足maxlen<=answer<=totlen/2 或者 answer==totlen
- 然后answer一定是totlen的约数即必须有 totlen%answer==0
- 其次给长度从大到小排序,这样可以避免无意义的搜索
- 同一长度的木棒具有同样的效果,即如果第一根该长度的木棒无法满足要求,则跳过所有该长度的木棒(因为结果都一样)
- 作为当前possibleanswer的开头第一个木棒必然是当前可用木棒集合中最长的,如果最长的都无法满足要求,则当前possibleanswer一定impossible。
如果当前碎木棒正好使得正在拼的木棒满足当前的possibleanswer,但是在它之后的其他木棒无法完成该possibleanswer,则正在拼的这根木棒一定无法满足该possibleanswer。
贴代码吧:
#include <cstdio>
#include <algorithm>
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int totnum, requirenum, singlelen, totlen, sticklen[64];
bool check(int curlen, int position, int finishnum);
int cmp(int a, int b) { return a>b; }
int main(int argc, char const *argv[])
{
while(scanf("%d", &totnum) != EOF && totnum != 0){
totlen = 0;
for(int i = 0; i < totnum; ++i){
scanf("%d", sticklen+i);
totlen += sticklen[i];
}
sort(sticklen, sticklen+totnum, cmp);
for(singlelen = sticklen[0]; singlelen <= totlen/2; ++singlelen){
if(totlen%singlelen==0) {
requirenum = totlen/singlelen;
if(check(0, 0, 0)) { break; }
}
}
if(singlelen>totlen/2){ printf("%d\n", totlen);}
else {printf("%d\n", singlelen);}
}
return 0;
}
bool check(int curlen, int position, int finishnum)
{
static bool flag[64] = {false};
bool ans = false;
if(curlen == singlelen){
if(++finishnum == requirenum) {
ans = true;
} else {
for(position=0;flag[position];++position);
flag[position] = true;
ans = check(sticklen[position], position+1, finishnum);
flag[position] = false;
}
} else if(curlen < singlelen) {
int prelen = 0, newlen;
for(int pos = position; pos<totnum; ++pos){
if(flag[pos] || sticklen[pos] == prelen) { continue; }
prelen = sticklen[pos];
newlen = curlen+prelen;
flag[pos] = true;
ans = check(newlen,pos+1,finishnum);
flag[pos] = false;
if(ans || newlen==singlelen || curlen == 0) { break; }
}
}
return ans;
}