蚂蚁搬沙(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; }