算法研究(三) 巧用快排

题目:给定一个具有n个元素的实数集A,一个实数t,一个整数k,请问如何快速的确定该实数集中是否存在一个具有k个元素的子集,其所有元素之和小于等于t?

对于这个问题,我首先想到的是排序,只要找到实数集中最小的k个元素,问题也就解了。但对于完全的排序而言实际上是没有必要的,我们只需要确定最小的k个元素就行了。于是我又想到了快排,它的分治思想用在这里是再合适不过了,我们只需对快排作一些改造可以得到一个很好的解决方案。

int partition(double *set, int n) {

    int low = 0, high = n - 1;
    double p = set[n - 1]; 
    while(low < high) {

        while(set[low] <= p && low < high )
            ++ low;
        if (low < high)
            set[high --] = set[low];
        while(set[high] > p && low < high)
            -- high;
        if (low < high)
            set[low ++] = set[high];
    }   
    set[low] = p;
    return low;
}

int sum(double* set, int n) {
        
    int i;
    double s = 0;
    for (i = 0; i < n; ++i)
        s += set[i];
    return s;
}

void print(double* set, int n) {

    int i;
    for (i = 0; i < n; ++i)
        printf("%lf\t", set[i]);
}

int verify(double *set, int n, int k, double t) {

    if (n < k || n == 0)
        return 0;

    int m = partition(set, n);
    if (0 == m)
        m = 1;

    if (m <= k) {
        double s = sum(set, m);
        if (m < k) {
            if (s <= t) {
                print(set, m);
                return verify(set + m, n - m, k - m, t - s);
            }
            else
                return 0;
        }
        else {
            if (s <= t) {
                print(set, m);
                printf("\n");
                return 1;
            }
            else
                return 0;
        }
    }
    else 
        return verify(set, m, k, t);
}

实际上,认真分析,会发现这里存在着一个问题,题目中给定的是实数集,因此,就存在t为负的情况。在这种情况下,我们可能会求出这样一个子集A1,其所有元素之和大于t,但一个包含A1的另一个子集A2,其所有元素之和可能反而小于t。例如:A = {-5, -3, -2, 1, 7}, t = -9, A1 = {-5, -3}, A2 = {-5, -3, -2}. 这样一来,我们在line48判断s<=t时就会失败从而函数返回假。
解决这个问题的办法就是当t<0时,将t和实数集A中的所有元素都取负,然后问题就转化成了:给定一个具有n个元素的实数集A,一个实数t(t>0),一个整数k,请问如何快速的确定该实数集中是否存在一个具有k个元素的子集,其所有元素之和大于t。然后使用类似的思想,问题也就解决了。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值