题解 UVA529 【Addition Chains】
大爆搜+剪枝
这题与NOI1999 生日蛋糕思路差不多,总的思路就是大爆搜,看似是不可能过的,但事实上又能过,太玄学了
相信看到这题都能想到思路是搜索,因为
下面来说一说各种剪枝以及优化
round 1
这题搜索思路多样,个人认为dfs好想也好写。因为保证对每个反正都是
round 2
首先是枚举顺序的的问题,楼上的部分题解建议从大到小枚举,因为最先枚举的数字越大越容易快速接近答案。但是我实测从大到小、从小到大两种枚举方法,差别不大,甚至从小到大枚举会更快一些。
round 3
可行性剪,枝因为题目要求序列是递增的,所以我们枚举到某一层时,如果这一层的数字比上一层的小,
round 4
可行性剪枝,序列递增,当枚举到某一层时,如果接下来的每一层都比上一层的数字大
round 5
可行性剪枝,我们知道,如果一个大的数由两个比它小的数相加得到,那么这个大的数不会大于较小的两个数中较大的那个数的二倍。由这个性质我们可以推得:当枚举到某一层时,如果接下来的每一层的数字都是上一层的数字的二倍,最后都比
round 6
以上的剪枝以及优化足以过掉该题的数据了,其实是我想不出来更好的剪枝了
以下是代码
#include <cstdio>
#include <iostream>
#include <map>
using namespace std;
int n,d,ans[30];
void print(){
for(int i=0;i<d;++i) printf("%d ",ans[i]);
printf("%d\n",ans[d]);
}
bool iddfs(int deep){
if(deep==d){
if(ans[d]==n) return true;
return false;
}
if(ans[deep]<=ans[deep-1]) return false; //剪枝3
if(ans[deep]+d-deep>n) return false; //剪枝4
if(ans[deep]*(1<<(d-deep)) <n) return false; //剪枝5
for(int i=0;i<=deep;++i){
for(int j=i;j<=deep;++j){
ans[deep+1]=ans[i]+ans[j];
if(iddfs(deep+1))return true;
}
}
return false;
}
int main(){
ans[0] = 1;
while(scanf("%d",&n)&&n){
for(d=0;true;++d){
if(iddfs(0)) break;
}
print();
}
return 0;
}
完结,撒花!