正文

Zju 1711 Sum It Up2006-09-13 21:53:00

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

分享到:

2065174 2006-09-13 21:04:57 Accepted 1711 C++ 00:00.00 436K St.Crux

    背政治背的太阳穴发疼,连啃着苹果都会自觉想到人与自然的对立统一。黑格尔先生、马克思先生、还有很多很多先生,若是在天有知,定会被几百年后的中国大学生精英们感动到流泪口牙。

    好吧,做一道1/2弱题换换脑子。题目还是很弱的,普通的搜索,几个数之和凑成M,不允许有重复出现。之所以加个头衔,是因为当初在学校比赛里做的时候用了很白痴的手段:把搜出来的结果换成string放到一个vector里,后来者要和先到的做比较......这有够烂的了。但在当时是我狗急跳墙,哦不,是灵机一动下的大彻大悟。

    但是如今就大不同哦。在清华版《数据结构》的理论光环指引下(注:这是广告),我写下了简洁的方法。

    举一个例子。

    M = 13。有六个数: 6, 5, 5, 2, 2, 2。

    13 = 6 + 5 + 2

    显然,搜完第一个2之后,下一个2应该被忽略。然而在搜索里总不能写if(a[i] != 2)吧。因为如果M是15,第二个2是必须的。这个处理困惑了我好久——查理曼的后代称之为Hanter,Angle-Sexons(盎格鲁萨克森人,又译棱角分明的性感男人)改进为Haunt,后来流传到中国称作「鬼啊」——这是迷信的象征。总之就是莫明其妙的出错。

    后来仔细的研究了几种遍历。发现如果这里采用后序遍历——也就是先记录子节点再记录根节点的话,问题就迎刃而解了。

    考虑上面的例子。遍历时顺序是这样的:2,5,6。这很好的解释了为什么第二个2不需要了——当回溯到5时只要看一下子节点记录(2)就知道了。注意,回溯。规律同样适用于6和5。

    比如M=15。M=6+5+2+2。但是没有回溯。当第二个2做完之后,第三个2加不了,因为17>15。于是回溯。这时5的子节点是2,然后K掉第三个2。

#include <cstdio>
#include <string>

int n, m, r[12], a[12], f, c, l;   

//r保存结果,a保存输入。c表示r的长度,即搜索的深度。l记录上一个遍历的点。

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

int main()
{
 //freopen("in.txt", "r", stdin);
 while(scanf("%d %d", &n, &m) && n)
 {
  printf("Sums of %d:\n", n);
  int i;
  for(i = 0; i < m; i ++)
  {
   scanf("%d", &a[i]);
  }
  init();
  dfs(-1, 0);
  if(!f)
   printf("NONE\n");
 }
 return 0;
}
    
void init()
{
 memset(r, 0, sizeof(r));
 f = 0, c = -1, l = 0;
}

void dfs(int i, int sum)
{
 c ++;
 if(sum == n)
  print();
 else
 {
  int ii = i + 1;
  for( ; ii < m; ii ++)
  {
   if(a[ii] + sum <= n && a[ii] != l)
   {
    r[c] = a[ii];
    dfs(ii, sum + a[ii]);
   }
  }
 }
 if(i != -1)
  l = a[i];
 c --;
}

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

阅读(4262) | 评论(1)


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

评论

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