1.T4合并果子(NOIP2004)
【问题描述】
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有3种果子,数目依次为1,2,9。可以先将 1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为 12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。
【输入文件】
输入文件fruit.in
包括两行,第一行是一个整数(n)((1 le n le 10000)),表示果子的种类数。第二行包含(n)个整数,用空格分隔,第(i)个整数(a_i)((1 le a_i le 20000))是第i种果子的数目。
【输出文件】
输出文件fruit.out
包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于(2^{31})。
【样例输入】
3
1 2 9
【样例输出】
15
【数据规模】
对于30%的数据,保证有(nle 1000);
对于50%的数据,保证有(nle 5000);
对于全部的数据,保证有(nle 10000)。
解析
依据题意,每次将重量最小的堆进行合并即可。
可以有如下错误代码:
for(int i = 1 ; i < n; i ++){
a[i+1] += a[i] ;
ans += a[i+1] ;
}
但是这个代码并不能AC。原因是每次进行合并后,有的堆不再是重量最小的堆。
便进行sort
,得到如下:
for(int i = 1 ; i < n; i ++){
sort(a+i,a+n+1);
a[i+1] += a[i] ;
ans += a[i+1] ;
}
嗯……TLE了6个点。
想到优化,便想到用(operatorname{STL})中的(operatorname{priority\_queue})。
定义为: priority_queue<Type, Container, Functional>name;
如: priority_queue<int>q;//默认为大根堆
priority_queue<int,vector<int>,less<int> >q;//大根堆
priority_queue<int,vector<int>,greater<int> >q;//小根堆
用法: q.top() 访问队头元素
q.empty() 队列是否为空
q.size() 返回队列内元素个数
q.push() 插入元素到队尾 (并排序)
q.pop() 弹出队头元素
AC代码
#include<bits/stdc++.h>
using namespace std;
int a,b,c,ans;
const int INF = 0x3f3f3f3f,N = 1;
int n;
priority_queue<int,vector<int>,greater<int> >q;
int main(){
int tmp;
scanf("%d",&n);
for(int i = 1 ; i <= n ; i ++)
scanf("%d",&tmp),q.push(tmp);
while(q.size() >= 2){
a = q.top(),q.pop();
b = q.top(),q.pop();
c = a + b;
q.push(c);
ans += c;
}
printf("%d",ans);
return 0;
}
2.T7运输(trans)
【问题描述】
现在已知 (N) 件商品。和搬运它们其中每一件的费用。现在搬家公司的老板Mr.SB 决定让
我们每次任意选取(2) 件商品。然后这(2) 件商品只算一件商品的费用。但是这个商品的搬运费
用是将选出的(2) 个商品的费用之和除以(K) 的运算结果。如此反复。直到只收一件商品的钱。
这个就是商店要付的费用。想尽可能的少付钱,以便将更多的钱卷给希望工程。所以请你帮
他计算一下最少只用付多少钱。
【输入格式】
(n,k)
(w_1,w_2,dots,w_n)(每一件商品的搬运费用)
【输出格式】
输出一个数字,表示最少付多少钱。
【输入样例】
5 2
1 2 3 4 5
【输出样例】
1
【数据规模】
(nle10000)
(kle10000)
解析
和上一题一样,只是题意描述过于不清……
推导:若第一轮选取(w_i)与(w_{i+1}),则价格(=frac{w_i + w_{i-1}}{k} = frac{1}{k} imes w_i+frac{1}{k} imes w_j)
第二轮选取第一轮的与(w_h),则价格(=(frac{1}{k})^2 imes w_i+(frac{1}k)^2 imes w_j+frac{1}k imes w_h)
第(n)轮为:((frac 1 k)^n imes w_i + dots+frac 1 k imes w_{...})
所以要使价格最小,要让最大的先被选取。
同前题,只是将小根堆换为大根堆即可。
AC代码
#include<bits/stdc++.h>
#include <queue>
using namespace std;
const int INF = 0x3f3f3f3f,N = 1e4+5;
int n,k,w[N];
bool cmp(int a,int b){
return a>b;
}
priority_queue<int,vector<int>,less<int> >q;
int main(){
int tmp;
scanf("%d %d",&n,&k);
for(int i = 1 ; i <= n ; i++)
scanf("%d",&tmp),
q.push(tmp);
int a,b,ans;
while(q.size()>=2){
a = q.top(),q.pop();
b = q.top(),q.pop();
ans = (a+b)/k;
q.push(ans);
}
printf("%d",ans);
return 0;
}