合并果子(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%的数据,保证有(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] ;
}

嗯……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

【数据规模】

(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;
}

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

原文地址:https://www.cnblogs.com/Shinomiya/p/14299532.html