合并果子(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] ;
}

嗯……TLE6个点。



想到优化,便想到用\(\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;
}

完结撒花✿✿ヽ(°▽°)ノ✿

posted @ 2021-01-19 19:19  Last-Order  阅读(399)  评论(0编辑  收藏  举报