【C++】贪心算法

文章介绍了如何使用贪心算法解决寻找给定集合中最大子集和的问题,通过逐步优化解法,从普通遍历到预处理数组,降低循环嵌套,提高代码效率。
摘要由CSDN通过智能技术生成

今天难得有时间来讲讲贪心算法。

题目引入

给定一集合,找出其中最大子集。
输入:两行,第一行输入n(n<1000),代表有n个数;第二行即是n个数的集合,每个元素用空格隔开。(对于100%的数据,整形,其绝对值不超过1000)。
输出:一行,直接输出最大子集合的和max。


解题

可以使用数组。
因为对于100%的数据,其绝对值不超过1000,所以max初始值可以设置为-1001,从而保证max会被更新,是数组中的数据。

普通解法(1.0版)

很明确,先在数组中遍历子集的首位(第一层循环),再从其首位开始遍历子集的末位(第二层循环),最后用遍历前遍历得到的首位到末位累加出当前子集的和x第三层循环),每次第三层循环结束后都更新一次max(如果xmax大, 就将x赋值给max)。

#include <iostream>
int n, a[1024], x, max=-1001;
int main(){
    scanf("%d", &n);
    for(int i=0; i<n; i++){
        scanf("%d", &a[i]);
    }
    for(int start=0; start<n; start++){
        for(int end=start; end<n; end++){
            x=0;
            for(int i=start; i<=end; i++){
                x+=a[i];
            }
            if(x>max){
                max=x;
            }
        }
    }
    printf("%d", max);
    return 0;
}


不走寻常路的我的解法(2.5版)

就是把上述思路的第二、三层循环合并了一下。
我们想,假如首位在a[0],第二层循环这次遍历到a[3],第三层循环就遍历a[0],a[1], a[2], a[3];下次就遍历到a[4],第三层循环:a[0], a[1], a[2], a[3],a[4]。其中第三层循环两次遍历中共同的a[0], a[1], a[2],a[3]可不可以省略呢?
当然可以。第二层循环遍历末位是从首位开始的;第三层循环亦然,且正好是到前第二层循环得到的末位结束。所以,就可以将第三层循环中的操作x+=a[i];直接放到第二层循环中(当然,重置x这一步x=0;也要外移)。

#include <iostream>
int n, a[1024], x, max=-1001;
int main(){
    scanf("%d", &n);
    for(int i=0; i<n; i++){
        scanf("%d", &a[i]);
    }
    for(int start=0; start<n; start++){
        x=0;
        for(int end=start; end<n; end++){
            x+=a[end];
            if(x>max){
                max=x;
            }
        }
    }
    printf("%d", max);
    return 0;
}


老师的初步优化的解法(2.0版)

最近老师老师在教我们预处理思想,这里果不其然也用上了。 思路:(别忘了, start:首位;
end:末位。(下面公式看不懂就看评论))
定义 : b n = ∑ i = 0 n a i 而 ∑ i = s t a r t e n d a i = ∑ i = 0 e n d a i − ∑ i = 0 s t a r t − 1 a i ⇒ ∑ i = s t a r t e n d a i = b e n d − b s t a r t − 1 定义: b_n = \sum_{i=0}^{n}{a_i}\\ \begin{aligned}\\ 而\sum_{i=start}^{end}{a_i} &=\sum_{i=0}^{end}{a_i} - \sum_{i=0}^{start-1}{a_i}\\ \Rightarrow \sum_{i=start}^{end}{a_i} &= b_{end} - b_{start-1}\\ \end{aligned}\\ 定义:bn=i=0naii=startendaii=startendai=i=0endaii=0start1ai=bendbstart1
数组b作为预处理,在输入的时候赋值。 这样我们就可以用前两层循环分别遍历首位和末位,最后直接使用预处理数组b计算出该子集。
注意,为了防止数组越界, b n b_n bn在程序中写成 b n + 1 b_{n+1} bn+1(相当于整体相对数组a往后移了一位),没有区别。

# include <iostream>
int a[1024], n, max=-1001, x, b[1024];
int main (){
    scanf("%d", &n);
    for (int i=0; i<n; i++){
        scanf("%d", &a[i]);
		b[i+1]=b[i]+a[i];
    }
	for (int start=0; start<n; start++){
		for (int end=start; end<n; end++){
			x=b[end+1]-b[start+1-1];
            if(x>max){
                max=x;
            }
		}
	}
    printf("%d", max);
    return 0;
}


使用贪心算法的解法(3.0版)

我们想,当集合中数组全为正数时,当子集合包含所有元素时最大,因为此时每个元素或多或少都对和有帮助(接地气的说)。可是,现实差强人意,会有负数的存在,负数对我们的和会起到副作用——那我们不要它就够了。我当时也是这么想,差点就自己思考出贪心算法来了,可现实差强人意——我判断当前元素是否为负数,如果是,就一同舍弃该项及以前的所有项,这显然不可行的。而贪心算法则是从集合首位开始累加,判断如果当前累加和是否小于零——如果小于零,就说明前面的连续子集都小于零,对和只有副作用,直接舍弃。

# include <iostream>
int a[1024], n, max=-1001, x;
int main (){
    scanf("%d", &n);
    for (int i=0; i<n; i++){
        scanf("%d", &a[i]);
    }
    for (int i=0; i<n; i++){
        x+=a[i];
        if (x<0){
            x=0;
        }
        if(x>max){
            max=x;
        }
    }
    printf("%d", max);
    return 0;
}


总结

我们不难发现,从1.0版本到3.0版本,思路从笨重到精炼、巧妙,代码的循环嵌套越来越少,代码运行速度也越来越快。这就是有算法支持的代码与普通的代码的差别。一名算法工程师,一天大约写100多行代码;代码不在多,在于精、在于效率、在于质量;我们要在完成之上,不断地追求完美。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值