贪心算法

贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。
贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
贪心算法的基本思路是从问题的某一个初始解出发一步一步地进行,根据某个优化测度,每一步都要确保能获得局部最优解。每一步只考虑一个数据,他的选取应该满足局部优化的条件。若下一个数据和部分最优解连在一起不再是可行解时,就不把该数据添加到部分解中,直到把所有数据枚举完,或者不能再添加算法停止。

例题:
题目描述

有n堆石子排成一排,第i堆石子有ai个石子。每次,你可以选择任意相邻的两堆石子进行合并,合并后的石子数量为两堆石子的和,消耗的体力等价于两堆石子中石子数少的那个。请问,将所有的石子合并成一堆,你所消耗的体力最小是多少?

输入描述

第一行是一个整数T(1≤T≤20)表示样例的个数。
每个样例的第一行是一个整数n(1≤n≤10000),表示石子堆的数量。
第二行是n个整数ai(1≤ai≤10^9)

输出描述

每一行输出样例结果

输入

2
2
1 2
1
1

输出

1
0

说明
巨大的输入,请用c风格进行输入

题目分析

对于n堆石子,我们要从中选择n-1次才能完成对所有石子的合并,那么,最为理想的状态,可以是一个不递减数列或者是一个不递增数列如:2 3 4 9 11 23 65或95 88 41 23 5 1,在这两种情况下,只要从最大的一端开始往左或往右合并,每次消耗掉的体力就是小的那一堆,同时因为每次都是剩余石子与当前最大的那一堆合并,那么这一堆合并后的石子依旧是最大的,所以在这两种情况下,合并所有石子需要消耗的体力是最小的,那就是除去最大那一堆的所有石子的重量的和,我们暂时把这时的消耗成为极限最小体力消耗MIN,那么当每堆石子的数量是打乱的情况下(没有稳定的递增递减关系时是否能通过人为的合并顺序达到极限最小消耗MIN呢?答案是肯定的,对于一个任意给出的数列,如:8 7 6 3 7 9 6,每次找到当前的最大堆,同时看一下它的两边,选二者中大的那一个与之合并,这样能保证数列的初始最大堆每一次都是被包含在当前最大堆当中,且每次都是由这个当前最大堆去和它左边或者右边的较小的堆合并,每次重复这个过程,通过这种方法所消耗的体力便与数列数有序时一样能以极限最小体力MIN将所有的石子堆合并。

代码:

#pragma warning(disable:4996)	//防止出现4996错误
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

typedef long long ll;

int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		int n, m, maxx = 0;
		scanf("%d", &n);
		ll sum = 0;
		for (int i = 1; i <= n; i++) {
			scanf("%d", &m);
			if (m > maxx) maxx = m;
			sum += m;
		}
		sum -= maxx;
		printf("%lld\n", sum);
	}
	return 0;
}
posted @ 2019-09-03 15:04  林深处见鹿  阅读(2695)  评论(0编辑  收藏  举报