最短加法链问题,POJ2248,BFS,搜索


该题也是算法导论的结课作业,看了一下貌似在POJ上有原题。

主要思路就是搜索,至于DFS还是BFS都可以,DFS需要加比较好的剪枝函数,不然会TLE。

宽搜BFS,因为要找到的是最短的加法链,宽搜是最快速的方法。算法的设计上,因为需要保存路径,所以用结构体nodeintid,int val,int pre)保存每个节点,根据preid的映射关系使得路径成为链式,再宽搜路径即可

核心代码代码:

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;
}

最近在搞云计算,更新的慢,见谅

点赞