合并果子(NOIP2004)(fruit) + 运输(trans)题解
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%的数据,保证有\(n\le 1000\);
对于50%的数据,保证有\(n\le 5000\);
对于全部的数据,保证有\(n\le 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
【数据规模】
\(n\le10000\)
\(k\le10000\)
解析
和上一题一样,只是题意描述过于不清……
推导:若第一轮选取\(w_i\)与\(w_{i+1}\),则价格\(=\frac{w_i + w_{i-1}}{k} = \frac{1}{k}\times w_i+\frac{1}{k}\times w_j\)
第二轮选取第一轮的与\(w_h\),则价格\(=(\frac{1}{k})^2\times w_i+(\frac{1}k)^2\times w_j+\frac{1}k\times w_h\)
第\(n\)轮为:\((\frac 1 k)^n\times w_i + \dots+\frac 1 k \times 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;
}