该题也是算法导论的结课作业,看了一下貌似在POJ上有原题。
主要思路就是搜索,至于DFS还是BFS都可以,DFS需要加比较好的剪枝函数,不然会TLE。
宽搜BFS,因为要找到的是最短的加法链,宽搜是最快速的方法。算法的设计上,因为需要保存路径,所以用结构体node(intid,int val,int pre)保存每个节点,根据pre和id的映射关系使得路径成为链式,再宽搜路径即可
核心代码代码:
void bfs()
{
q.push(node(0,1,-1));
path[cnt++] = node(0,1,-1);
node now,next;
while(!q.empty())
{
now = q.front();
q.pop();
//now.print();
for(int i = now.id; i!=-1; i = path[i].pre)
{
int tmp = now.val + path[i].val;
if(tmp == n)
{
cout<<countPath(now.id)<<endl;
cout<<n<<" ";
output(now.id);
cout<<endl;
return;
}
if(tmp < n)
{
next = node(cnt, tmp, now.id);
path[cnt++] = next;
q.push(next);
}
}
}
}
但是,由于每一层需要将该层的所有路径全部保存,而这些路径不能被清除,所以空间复杂度极其大,当n等于200,即200的加法链需要开辟10000的数组以存储路径,算法并不可行。
深度优先搜索
使用深搜相对于广搜的有点之处在于空间复杂度上会有优化,每一条深度上的路径只需要保存该路径即可。但是当加法链扩散出去后,时间复杂度上会程指数增长,所以得加以剪枝函数。
一开始考虑较为简单的剪枝函数,即当当前搜索的层数小于已经搜出的最短答案时返回。即当l>=ans的时候返回。
但是该剪枝效果也不是很好,当n大于200时,时间明显增长。之后我选择了另一种剪枝,剪枝效果更好。
假设当前搜到的层数为l,当前搜到的数为t目标数为n,则从t到n最短的加法链则是t->t*2->t*4..->n,即每次都乘2,长度是,所以当l+log2(n/t)<=ans,就可以返回了。没必要每次都运算,而对于这个长度,可以实现打出从i到j的最短路径长度以供每次直接以o(1)复杂度获取。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
#include <queue>
#include <ctime>
#include <set>
using namespace std;
#define MAX 10005
int a[MAX],r[MAX], b[2*MAX];
int n,ans;
void init(int n)
{
int i;
ans = n;
a[0] = 1;
for(i=n; i<=n+n; i++)
b[i] = 0;
for(i=n-1; i>0; i--)
b[i]=b[i+i]+1; //b[i]中记录着到当前数最短的距离数
}
void printf()
{
int i;
for(i=0; i<ans; i++)
cout<<r[i]<<” “;
cout<<n<<endl;
cout<<ans<<endl;
}
void dfs(int l)
{
int i, k;
if(l+b[a[l]]>=ans)
return;
if(a[l]==n)
{
ans = l;
for(i=0; i<l; i++)
r[i]=a[i];
return;
}
for(i=l; i>=0; i--)
for(k=i; k>=0; k--)
{
a[l+1]=a[i]+a[k];
if(a[l+1]>a[l]&&a[l+1]<=n)
dfs(l+1);
}
}
int main()
{
while(cin>>n)
{
init(n);
dfs(0);
printf();
}
return 0;
}
最近在搞云计算,更新的慢,见谅