我感觉有点糟糕,尤其是我对问题所做的错误编辑......无论如何:
首先,解决这个问题的最天真、最直接、最直接的方法;这将从集合中的第一个数字开始,计算每一步的nth 部分总和,将该部分总和与从第三个元素开始的每个元素的双精度进行比较,直到 nth 元素.如果比较对每个元素都成立,则将当前最后一个标记为最大值z。
下面的宏 largestzfinder,依赖于函数 largestzfinderfunc 这样做:
// indices are zero based
#define largestzfinder(_x_) largestzfinderfunc((_x_), sizeof(_x_) / sizeof(*_x_))
unsigned int largestzfinderfunc( unsigned int set[], size_t size ) {
unsigned int largestz = 0;
unsigned int partialsumsofar = 0;
int disqualified;
for ( int i = 0; i < size; i++ ){
partialsumsofar += set[i];
disqualified = 0;
for ( int j = 2; j <= i; j++ ) { // for all j from 2 to i (inclusive)
if ( 2 * set[j] > partialsumsofar ) {
disqualified = 1;
break;
}
}
if ( !disqualified ) // if comparison held for all j
largestz = i;
}
return largestz;
}
使用这种方法,我们从 最小的 z 开始逐渐到达我们的largestz,然后找到下一个更大的 z,直到我们到达 最大。我们可以从头开始简化该过程,在这种情况下,除了 最大的 之外,我们不必遍历所有其他 zs,并且我们不需要其他的要么……
为此,我们需要预先计算整个和,然后从最后一个元素开始逐个减少,以同样的方式与缩小的部分和进行比较,并在找到后立即返回答案非disqualified 候选人。下面的代码就是这样做的:
// indices are zero based
#define largestzfinder(_x_) largestzfinderfunc((_x_), sizeof(_x_) / sizeof(*_x_))
unsigned int largestzfinderfunc( unsigned int set[], size_t size ) {
unsigned int largestz = 0;
unsigned int partialsumsofar = 0;
int disqualified;
for ( int i = 0; i < size; i++ )
partialsumsofar += set[i];
for ( int i = size - 1; i >= 0; i-- ){
disqualified = 0;
for ( int j = 2; j <= i; j++ ) { // for all j from 2 to i (inclusive)
if ( 2 * set[j] > partialsumsofar ) {
disqualified = 1;
break;
}
}
if ( !disqualified ) { // if comparison held for all j
largestz = i;
break;
}
partialsumsofar -= set[i]; // updates/reduces partialsumsofar
}
return largestz;
}
现在,您看,我们检查条件是否适用于从第 3 个到最后一个的每个元素,一个接一个...而我们可以只检查其中最大的一个!如果largestamongthem <= partialsumsofar / 2,那么它们都将小于或等于到目前为止的部分总和。
您将如何确定largestsofar?好吧,事情变得复杂了,尤其是当您从头开始执行该过程时。如果我们从一开始就这样做,那么我们可以从 largestsofar = 0; 开始,然后将每个后续元素与它进行比较,更新我们的 largestsofar。
一种方法是创建一个与集合数组具有相同大小的新整数数组,它将为每个点保存largestsofar。以下代码使用该方法:
// indices are zero based
#define largestzfinder(_x_) largestzfinderfunc((_x_), sizeof(_x_) / sizeof(*_x_))
unsigned int largestzfinderfunc( unsigned int set[], size_t size ) {
unsigned int largestz = 0;
unsigned int partialsumsofar = 0;
unsigned int * largestsofar = calloc( size, sizeof * largestsofar );
for ( int i = 0; i < size; i++ )
partialsumsofar += set[i];
largestsofar[0] = 0;
largestsofar[1] = 0;
largestsofar[2] = set[2];
for ( int i = 3; i < size; i++ )
largestsofar[i] = (set[i] > largestsofar[i-1]) ? set[i] : largestsofar[i-1];
for ( int i = size - 1; i >= 0; i-- ){
if ( 2 * largestsofar[i] <= partialsumsofar ) {
largestz = i;
break;
}
partialsumsofar -= set[i];
}
free( largestsofar );
return largestz;
}
好吧,如果您对分配内存不满意,请在其中保留迄今为止最大数字的列表,我支持您。下面的递归函数做的事情,没有保留一个列表。它看起来也短得多。缺点是它对读者不太友好。这里:
// indices are zero based
#define largestzfinder(_x_) largestzfinderfunc((_x_), sizeof(_x_) / sizeof(*_x_), (_x_)[0] + (_x_)[1], 1, 0)
unsigned int largestzfinderfunc( unsigned int set[], size_t size, unsigned int partialsumsofar, unsigned int i, unsigned int largestsofar ){
unsigned int j;
if ( i < size - 1 && ( j = largestzfinderfunc( set, size, partialsumsofar + set[i + 1], i + 1, ( set[i + 1] > largestsofar ) ? set[i + 1] : largestsofar ) ) ){
return j;
}
else if ( 2 * largestsofar <= partialsumsofar ) {
return i;
}
else
return 0;
}
让我简要解释一下它的作用:通过调用宏,它首先将r_0 + r_1 作为partialsumsofar,1 作为要检查的当前z,并将0 作为最大的数字,直到r_1(这是因为在比较时不考虑r_0 和r_1)。
但是,它不会立即检查;如果这不是最后一个元素 (i < size - 1),它将调用该函数一层,更新partialsumsofar,当前 z 被检查递增,最大数字也被更新。这将一直持续到到达最后一个元素。
之后,递归树将崩溃。如果最后一个不满足条件,它会返回一个0,导致父递归分支检查他/她是否满足条件。
只要一个父节点满足条件,它就会返回它当前的 z,导致每个父节点都返回相同的 z,因为它们都将返回的值存储在 j 下并返回它,如果 j 不为零.
本质上,它确实为每个largestsumsofar 和partialsumsofar 分配内存,但是当递归树/行/任何东西崩溃时,它会一一释放所有这些。
这不是那么简短,但无论如何,我希望这次我做对了。我现在想解决这个问题...