Addition Chains
题目描述:
一个与 n 有关的整数加成序列 < a0 , a1 , a2 ...am> 满足一下四个条件:
1.a0=1
2.am=n
3.a0<a1<a2<...<am
4.对于每个( 1≤k≤m )都存在有两个整数 i 和 j (0 ≤ i , j ≤ k - 1 , i 和 j可以相等),使得ak=ai+aj
你的任务是:给定一个整数 n ,找出符合上述四个条件的长度最小的整数加成序列。如果有多个满足要求的答案,只需要输出任意一个解即可。
举个例子:序列< 1,2,3,5 >和< 1,2,4,5 >均为 n = 5 时的解
输入格式:
多组数据,每行给定一个正整数n。输入以0结束。
输出格式:
对于每组数据,输出满足条件的长度最小的数列。
样例输入:
5
7
12
15
77
0
样例输出:
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
思路:
看了书之后才知道这题是用搜索。(蒟蒻的我只会暴力枚举)
那么怎么搜索又成为了一个问题——其实可以依次搜索一位k, 枚举之前的i, j, 把a[i] + a[j] 加到a[k]的位置上, 然后接着搜索;
这样还是AC不了。
这时就需要考虑剪枝。
对于剪枝:
①尽量从达到小枚举i,j让序列的数尽快逼近n;(贪心思想)
②为了不重复搜索,用一个bool数组存a[i] + a[j] 是否已经被搜过;
AC代码:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<iostream>
#include<stack>
#include<queue>
#include<deque>
#include<vector>
#include<map>
#include<set>
using namespace std;
#define maxn 1100
int n,ans,a[maxn];
bool use[maxn];
inline int read()
{
char kr=0;
char ls;
for(;ls>'9'||ls<'0';kr=ls,ls=getchar());
int xs=0;
for(;ls>='0'&&ls<='9';ls=getchar())
{
xs=xs*10+ls-48;
}
if(kr=='-') xs=0-xs;
return xs;
}
inline bool dfs(int stp)
{
memset(use,0,sizeof(use));
if(stp>ans)
{
if(a[ans]==n) return true;
else return false;
}
for(register int i=stp-1;i>=1;i--)
{
for(register int j=i;j>=1;j--)
{
if(a[i]+a[j]>n) continue;
if(!use[a[i]+a[j]])
{
if(a[i]+a[j]<=a[stp-1]) return false;
use[a[i]+a[j]]=true;
a[stp]=a[i]+a[j];
if(dfs(stp+1)) return true;
a[stp]=0;
use[a[i]+a[j]]=false;
}
}
}
}
int main()
{
while(n=read(),n!=EOF)
{
if(n==0) return 0;
if(n==1)
{
printf("1\n");
continue;
}
if(n==2)
{
printf("1 2\n");
continue;
}//特判一下上述三种情况
a[1]=1;a[2]=2;
for(ans=3;!dfs(3);ans++);//搜索记录
for(register int i=1;i<=ans;i++)
{
printf("%d ",a[i]);
}
printf("\n");
memset(a,0,sizeof(a));
}
return 0;
}
洛谷谜一般的评测TLE,在POJ上提交AC了。