Addition Chains
题目描述
一个与
n
n
n 有关的整数加成序列
<
a
0
,
a
1
,
a
2
,
.
.
.
,
a
m
>
<a_0,a_1,a_2,...,a_m>
<a0,a1,a2,...,am> 满足以下四个条件:
1.
a
0
=
1
1.a_0=1
1.a0=1
2.
a
m
=
n
2.a_m=n
2.am=n
3.
a
0
<
a
1
<
a
2
<
.
.
.
<
a
m
−
1
<
a
m
3.a_0<a_1<a_2<...<a_{m-1}<a_m
3.a0<a1<a2<...<am−1<am
4.
4.
4. 对于每一个
k
(
1
≤
k
≤
m
)
k(1≤k≤m)
k(1≤k≤m) 都存在有两个整数
i
i
i 和
j
(
0
≤
i
,
j
≤
k
−
1
,
i
j(0≤i,j≤k-1,i
j(0≤i,j≤k−1,i 和
j
j
j 可以相等
)
)
) ,使得
a
k
=
a
i
+
a
j
a_k=a_i+a_j
ak=ai+aj
你的任务是:给定一个整数
n
n
n ,找出符合上述四个条件的长度最小的整数加成序列。如果有多个满足要求的答案,只需要输出任意一个解即可。
举个例子,序列
<
1
,
2
,
3
,
5
>
<1,2,3,5>
<1,2,3,5> 和
<
1
,
2
,
4
,
5
>
<1,2,4,5>
<1,2,4,5> 均为
n
=
5
n=5
n=5 时的解。
输入格式
输入包含多组数据。每组数据仅一行包含一个整数 n ( 1 ≤ n ≤ 10000 ) n(1≤n≤10000) n(1≤n≤10000) 。在最后一组数据之后是一个 0 0 0 。
输出格式
对于每组数据,输出一行所求的整数加成序列,每个整数之间以空格隔开。
样例输入 #1
5
7
12
15
77
0
样例输出 #1
1 2 4 5
1 2 4 6 7
1 2 4 8 12
1 2 4 5 10 15
1 2 4 8 9 17 34 68 77
代码
#include<bits/stdc++.h>
using namespace std;
int n,a[1000],maxd=0;
bool dfs(int k){
if (k>maxd){
if (a[k-1]==n) return true;
else return false;
}
for (int i=k-1;i>=1;i--){
for (int j=k-1;j>=i;j--){
int tmp=a[i]+a[j];
if (tmp<=a[k-1]) continue;
a[k]=tmp;
if (dfs(k+1)) return true;
}
}
return false;
}
void solve(){
a[1]=1;
for (maxd=1;;maxd++) if (dfs(2)) break;
for (int i=1;i<=maxd;i++) cout<<a[i]<<' ';
cout<<endl;
}
int main(){
while(scanf("%d",&n)==1 and n!=0)
solve();
return 0;
}
分析
读题
此题我们并不知道要一个多长的序列凑出n
来,即dfs遍历的解答树的深度很难确定,极容易TLE
但我们知道要求一个最小的n
,但bfs需要超大的内存,极容易MLE
,不妨试一试
I
D
A
∗
IDA*
IDA∗算法(或者说迭代加深搜索),即在自己限制了解答树的深度情况下(在此题中,我们限制输出的序列长度),使用dfs求解.
代码( m a i n ( ) main( ) main()函数)
int main(){
while(scanf("%d",&n)==1 and n!=0)
solve();
return 0;
}
简单易懂,每次读入n,判断n是否是0,若是,则执行solve()函数,求出数列并输出.
代码( s o l v e ( ) solve( ) solve()函数)
void solve(){
a[1]=1;
for (maxd=1;;maxd++) if (dfs(2)) break;
for (int i=1;i<=maxd;i++) cout<<a[i]<<' ';
cout<<endl;
}
我们引入a[]
数组,表示最终的答案数组,由于题目规定,我们输出的序列的第一个数一定是1
,所以执行a[1]=1;
,作为对a[]数组初始化,同时,我们定义maxd,表示输出序列的最大长度,dfs将填充a[]数组,若dfs返回true
,表示已找到正确序列,否则增加序列长度,再次dfs直到返回true
.
由于序列的长度上线是逐步增加的,那么对于第一个使dfs返回true的maxd
一定是最小的序列长度.
代码( d f s ( ) dfs( ) dfs()函数)
bool dfs(int k){
if (k>maxd){
if (a[k-1]==n) return true;
else return false;
}
for (int i=k-1;i>=1;i--){
for (int j=k-1;j>=i;j--){
int tmp=a[i]+a[j];
if (tmp<=a[k-1]) continue;
a[k]=tmp;
if (dfs(k+1)) return true;
}
}
return false;
}
k表示dfs正在填充a[]数组的第k个数,若k>maxd,说明已经超出了我们的限制,应立即判断,对a[k-1]进行判断,若已经为n,返回true即可,否则返回false
由于题目要求,在填的第k个数应满足
a
k
=
a
i
+
a
j
(
0
≤
i
,
j
≤
k
−
1
)
a_k=a_i+a_j \ \ (0≤i,j≤k-1)
ak=ai+aj (0≤i,j≤k−1)
所以定义i与j,从k-1开始倒着枚举,为了尽快凑出n,一定是大的数与大的数相加会更快凑出n,所以倒着枚举.
对于tmp便是临时的
a
[
k
]
a[k]
a[k],由于要求一个升序的序列,要判断tmp与a[k-1]的关系,若不满足,不选择tmp填充a[k].
Final
一道经典的IDA*题,比起埃及分数简单了不少,迭代加深搜索虽然用dfs实现,但遍历树的方式却像bfs一样一层一层的遍历一棵树.