#10021. 「一本通 1.3 例 4」Addition Chains

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<...<am1<am
4. 4. 4. 对于每一个 k ( 1 ≤ k ≤ m ) k(1≤k≤m) k(1km) 都存在有两个整数 i i i j ( 0 ≤ i , j ≤ k − 1 , i j(0≤i,j≤k-1,i j(0i,jk1,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(1n10000) 。在最后一组数据之后是一个 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  (0i,jk1)
所以定义i与j,从k-1开始倒着枚举,为了尽快凑出n,一定是大的数与大的数相加会更快凑出n,所以倒着枚举.
对于tmp便是临时的 a [ k ] a[k] a[k],由于要求一个升序的序列,要判断tmp与a[k-1]的关系,若不满足,不选择tmp填充a[k].

Final

一道经典的IDA*题,比起埃及分数简单了不少,迭代加深搜索虽然用dfs实现,但遍历树的方式却像bfs一样一层一层的遍历一棵树.

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值