题目大意如下:
一个数列满足下列四种性质
1、a0 = 1
2、•am = n
3、•a0 < a1 < a2 < ... < am-1 < am
4、对于每一个K(1 <= k <= m),这里有两个数字,i,j,(他们可能相同也可能不同)(0 <= i, j <= k-1),有ak = ai + aj ;
现在给你一个n,让你构造出一个最短的满足上述四种性质的数列
解题思路:
一开始觉得应该用BFS,因为是求最优解,想把1---n的数字按递增的顺序入队列,然后一个一个的弹出,放在相应的位置。
但是由于每一个位置确定数字的方式种类会变动,所以没有写出来。然后决定用DFS。
前面两条性质基本不用管,重要的是3 4 条性质。尤其是第四条,ak是由ai aj加起来构成,而i j 的范围也已经给出。假设现在
k==2,那么它可以由a0 a1, a1 a1 ,a0 a0,三种形式构成。所以在下标移动的时候,i从0开始,j从k-1开始,j不动,i移动,即可
得出ak的组合情况。
为了满足递增序列条件,每一项我都按照a0+ak-1来构造的,比如,当n==5的时候
a0 a1 a2 a3
1 2 3 4
搜索的思想也就是从这里开始了,构造这个数列有很多步,每一步有很多种可能,处理好K步,然后再处理k+1步,如果不行,那就
返回上一步重新选择可行的方案继续进行。过程中当然也要 用到标记数组起到剪枝的作用。
而题目中最后一个要求是要求最短的,也就是说,当我们找到一种答案的时候还要判断它是不是最短的,如果不是,那么要继续
找,如果是最短的,那么当然就要退出循环。而每次一要开始判定并找寻最短序列的条件,就是当a0+ak-1==n的时候,因为此时
如果出现这个条件,则证明数列已经按基本要求构造完成了,除了最短这个条件之外。
以n==5为例来讲,当知道a0+a3==n的时候,就退出,把a3置0,然后看看三个数值的时候构造的这个数列是否满足要求。如果现在
假设的是三个数值的情况,那么很自然的在1 2 3这个序列中要找到==n的组合。a0+a2==4 a1+a2==5,依据循环可以得到==N的组合,
那么我们也就找到了更短的那个答案序列,接下来要做的就是更新最小长度,然后把现在找的的数列存起来。
之后,我们还是一样的思路,想找更短的,把a2划掉置0,然后a1+a1==4变成a0 a1 a2,我们知道,现在最短的长度是3
那么当前的情况肯定还是要向长度为4的方向搜索的,显然不对了,即使再搜到长度为3的另一种答案我们也不需要了,因为
答案可能不止一种,但是我们只需要求出其中一个就可以了。
所以要加上相应的控制条件。
代码如下:
//Addition Chains // //题目链接:http://acm.hdu.edu.cn/webcontest/contest_showproblem.php?pid=1009&ojid=2&cid=4532&hide=0 /* #include<iostream> #include<cstdlib> #include<stdio.h> #include<memory.h> using namespace std; int num[11000],temp[11000]; int minlength; int n; void dfs(int pos) { int t; int i,j,k; if(pos>=minlength) return ; for(i=0;i<pos;i++) { t=num[i]+num[pos-1]; if(t==n) { if(pos<minlength) { memset(temp,0,sizeof(temp)); minlength=pos; for(j=0;j<pos;j++) temp[j]=num[j]; } return ; } if(t<n) { num[pos]=t; dfs(pos+1); num[pos]=0; } } } int main() { // #ifndef ONLINE_JUDGE //freopen("f:\\in.txt","r",stdin); //#endif int i,j,k; while(scanf("%d",&n),n) { if(n==1) { printf("1\n"); continue; } memset(num,0,sizeof(num)); memset(temp,0,sizeof(temp)); minlength=999; temp[0]=num[0]=1; dfs(1); for(i=0;i<minlength;i++) { if(i==0) printf("%d",temp[i]); else printf(" %d",temp[i]); } printf(" %d\n",n); } return 0; }