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");
}
评论