正文

Zju 1937 Addition Chains2006-09-15 23:02:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/cruxd/18558.html

分享到:

2068885 2006-09-15 22:28:27 Accepted 1937 C++ 00:00.02 436K St.Crux

    给定队列首数1和尾数N,求一个最短升序队列,之中每一个数都可以用队列中其余两个数(可以相等)之和表示。

    这题相当的棒。似乎是枚举构造orz,然而可以用搜索dfs,搜到(加到)N时停止并判断长度。但如果用一个二重循环不断搜索,下场就是死。所以还需要优化。

    首先需要计算出,对于队列的最后第二个数M,累计到N最小需要多少步。因为2M到M需要一步,所以s[M] = s[M + M] + 1。这是题目的关键。比如49到98是一步,到100就至少要两步。

    然后剪枝:如果s[M] + 目前的长度已经不可能再得到最优解时停止。这种剪枝非常常见。我一开始没有考虑SM的情况,结果很惨。

#include <cstdio>

int n, ans, a[100], r[100], d[202];

void dfs(int);
void print();
void init();

int main()
{
 //freopen("in.txt", "r", stdin);
 while(scanf("%d", &n) && n)
 {
  init();
  dfs(0);
  print();
 }
 return 0;
}

void init()
{
 int i;
 ans = n;
 a[0] = 1; 
 for(i = n; i <= n + n; i ++) d[i] = 0;
 for(i = n - 1; i > 0; i --)  d[i] = d[i + i] + 1;
}

void print()
{
 int i;
 for(i = 0; i < ans; i ++)
 {
  printf("%d ", r[i]);
 }
 printf("%d\n", n);
}

void dfs(int l)
{
 int i, k; 
 if(l + d[a[l]] >= ans)
  return;  //prunation
 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);
  }
 }
}

阅读(4263) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册