zoukankan      html  css  js  c++  java
  • 合并果子(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;
    }
    

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

  • 相关阅读:
    【转载】多个集合合并成没有交集的集合-实现
    [遇见时光]美团测试实习生面试
    [遇见时光]中科院分词工具NLPIR,Not valid license or your license expired!
    html a标签的target属性
    使用EditPlus技巧,提高工作效率(自动文成文件、语法文件下载)
    android textView 替文字添加下划线 删除线
    Android把自己应用加入到系统文件分享中
    addFooterView 方法注意调用顺序
    android打开当前应用市场简单方法 (ActivityNotFoundException 异常解决)
    The currently displayed page contains invalid values异常
  • 原文地址:https://www.cnblogs.com/Shinomiya/p/14299532.html
Copyright © 2011-2022 走看看