蚂蚁搬沙(2017佛山市选拔初中组)

蚂蚁搬沙(2017佛山市选拔初中组)

题目描述

山谷中住着一个巨大的蚂蚁王国,蚁穴外有一个整洁的广场,天气晴好时蚁群常在那里举行各种活动。这天夜里,天降沙尘,第2天,广场上堆满了大大小小的沙堆,蚁哨出去数了数共有n堆,蚁后要求她的臣民将广场上的沙堆清理掉。具体办法是:每次可以把广场上的任意k堆沙子合并成一堆,重复进行直至所有的沙堆最终合并成一堆。规定(1):2≤k≤m,m由蚁后指定,(2):每次合并k堆沙子的代价是这k堆沙子的重量和。 
你的任务是,对给定的n和m,计算出将n堆沙子最终合并成1堆的最小总代价。 
例如,广场上有7堆沙子,其重量分别为45,13,12,16,9,5,22。当m=3时,这些沙堆合并成一堆的最小总代价为199。当m=5时,这些沙堆合并成一堆的最小总代价为148。 

输入格式 1757.in

第一行2个正整数,分别表示n(n≤100000)堆沙子和每次合并时可以合并的最大堆数m,从第二行开始有n个数,表示n堆沙子的重量(1~500),数与数之间用空格隔开。

输出格式 1757.out

只包含一个正整数,表示将n堆沙子合并成1堆所需的最小总代价。

输入样例 1757.in

7 3 
45 13 12 16 9 5 22 

输出样例 1757.out

199

 

  一开始做这题的时候,我连样例都看了半天。首先,由5+9+12=26,再由13+16+22=51,最后26+45+51=122,最终答案为26+51+122=199。

  所以我们会发现,在前面加的,会被重复累加更多次,所以每一次优先堆的最好越小越好,而且越多。

    这题用到优先队列,我写代码的时候犯了一个小小的错误,把普通队列的定义误以为是优先队列priority_queue<int> s;(一开始定义了queue<int> s),对优先队列的使用还不太熟悉。

    如果当(n-1)%(m-1)没有余数时,则从小的取起,每一堆都放m个,这样求出来的就是最大值。

    但是如果有多余的呢?如果前面一堆都有m个,那最后的累加部分就会变多,不是最优的。所以,我们要让重的尽量添加少的次数。算出n%(m-1),把多余的部分垒成一堆,把特殊的情况变回原来熟悉的(n-1)%(m-1)== 0,这样子又可以重复回上面的操作了。

#include<iostream>
#include<algorithm>
#include<queue>
#include<cstdio>
using namespace std;
int n,m,a,ans;
priority_queue<int> s;
int main()
{
	freopen("1757.in","r",stdin);
	freopen("1757.out","w",stdout);
	cin>>n>>m;
	for(int i=1;i<=n;i++) 
	{
		cin>>a;
		s.push(-a);
	}
	if((n-1)%(m-1)==0)
	{
		int k=(n-1)/(m-1);
		while(k)
		{
			int now=0;
			for(int i=1;i<=m;i++)
			{
				now=now+s.top();
				s.pop();
				
			}
			ans+=now;
			s.push(now);
			k--;
		}
	}
	else
	{
		int k=n%(m-1),now=0;
		for(int i=1;i<=k;i++)
		{
			now=now+s.top();
			s.pop();
		}
		ans+=now;
		s.push(now);
		int v=(n-k)/(m-1);
		while(v)
		{
			int now=0;
			for(int i=1;i<=m;i++)
			{
				now=now+s.top();
				s.pop();
				
			}
			ans+=now;
			s.push(now);
			v--;
		}
	}
	cout<<-ans<<endl;
	return 0;
}

 

posted @ 2017-08-19 16:37  yiyiyizqy  阅读(507)  评论(0编辑  收藏  举报