题目链接:切割 - 题目 - Daimayuan Online Judge

数据加强版链接: [NOIP2004 提高组] 合并果子 加强版 - 洛谷

题目描述

有一个长度为 ∑ai 的木板,需要切割成 n 段,每段木板的长度分别为 a1,a2,…,an。

每次切割,会产生大小为被切割木板长度的开销。

请你求出将此木板切割成如上 nn 段的最小开销。

输入格式

第 1 行一个正整数表示 n。

第 2 行包含 nn 个正整数,即 a1,a2,…,an。

输出格式

输出一个正整数,表示最小开销。

数据范围

对于全部测试数据,满足 1≤n,ai≤10^5。

样例输入:

5

5 3 4 4 4

样例输出:

47

nlogn解法 

核心思想:贪心

正向考虑题意的话,需要每次将长木板较平均的分割成两块,再每次分割出里面最小的,怎么才最平均呢?还得找最大值?个人觉得不是那么好处理,可以考虑下逆向思维,转换一下题意。

如何转换题意呢?将一块长木板分割为n段,每次的花费为被分割的木板长度,可以等价于被分割成的两块合成一块时,花费为合成的两块的长度和,便转化成了怎样使它合并成一块的花费最小问题。(举个例子,就比如一个长为4的分成一个1一个3,花费为4,跟一个1和一个3合并成一个4,花费为1+3时等价的)

思路:

考虑每次取出两个最小的合成一个更大的,直到最后只剩一个。

证明:

怎么证明这个贪心是对的呢?我们可以假设有三个木块a1<a2<a3,如果取a1,a2合并,需要的花费为(a1+a2)+(a1+a2+a3),如果不取两个最小的,而取a2,a3,需要花费为(a2+a3)+(a2+a3+a1)显然比第一种要大。那么如何推广到一般情况呢?我们可以这样想,合并了两个之后,费用肯定要加上两个的和,两个合并成的一个肯定还需要与其他的合并,而用递归去想这一部分的花费可以看成是大小固定的,就是说你合并成的还需要去和其他的合并求和,而最终下次合并的和是相同的,那么让两个合并的花费尽量小,花费不就小了吗?

代码实现

怎样每次找到两个最小的呢,并加入合并成的那个?我们考虑使用STL自带的最小堆-优先队列priority_queue。

复杂度分析:

优先队列的插入查询均为logn,复杂度为O(n)*O(logn)即O(nlogn)。

代码: 

#include <bits/stdc++.h>
using namespace std;
#define int long long //会爆int,所以改为了longlong
priority_queue<int, vector<int>, greater<int>> q; //小根堆
int n, ans;
signed main()
{
scanf("%lld", &n);
for (; n--;)
{
int x;
scanf("%lld", &x);
q.push(x); //初始将n个木块加入
}
while (q.size() >= 2)
{
int x1, x2;
x1 = q.top(), q.pop(); //取出两次堆顶
x2 = q.top(), q.pop();
ans += (x1 + x2);
q.push(x1 + x2); //加入合并的木块
}
cout << ans << "\n";
}

O(n)解法 

考虑优化掉每次插入查询的logn。每次合并成的新的木板肯定是载增大的,也就是说合成的木板是有序的,那么我们使没有被合并的那些木板变得有序,每次考虑取两者队首元素中较小的,用两个队列维护,因为有序所以队首元素为最小值。对初始队列的排序考虑桶排。可以在On的时间内完成此题了。(洛谷貌似卡读入了,所以加了个快读)

详见代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll a[100009]; //记录大小为i的木板的数量(桶排)
ll ans;
void read(int &x) //优化读入
{
int f = 1;
x = 0;
char s = getchar();
while (s < '0' || s > '9')
{
if (s == '-')
f = -1;
s = getchar();
}
while (s >= '0' && s <= '9')
{
x = x * 10 + s - '0';
s = getchar();
}
x *= f;
}
int main()
{
int n;
read(n);
for (int i = 1; i <= n; i++)
{
int x;
read(x);
a[x]++; //大小为x的数量+1
}
queue<ll> pre, added; // pre为原始的木板队列,added为后来合并加入的队列
for (int i = 1; i <= 100000; i++)
{
while (a[i]--) //因为i可能不止一个
{
pre.push(i); //放入队列中,使得pre是有序的
}
}
for (int i = 1; i <= n - 1; i++) // n个需要合并n-1次
{
ll x1, x2;
if ((!pre.empty() && !added.empty() && pre.front() < added.front()) || added.empty()) // pre的队首小于added的队首或者added为空
{
x1 = pre.front(); //从pre取
pre.pop();
}
else
{
x1 = added.front(); //从added取
added.pop();
}
//重复一次操作取x2
if ((!pre.empty() && !added.empty() && pre.front() < added.front()) || added.empty()) // pre的队首小于added的队首或者added为空
{
x2 = pre.front();
pre.pop();
}
else
{
x2 = added.front();
added.pop();
}
ans += (x1 + x2); //加上花费
added.push(x1 + x2); // added中加入新合成的木板
}
cout << ans;
}

代码源 每日一题 分割 洛谷 P6033合并果子的更多相关文章

  1. 堆学习笔记(未完待续)(洛谷p1090合并果子)

    上次讲了堆,别人都说极其简单,我却没学过,今天又听dalao们讲图论,最短路又用堆优化,问懂了没,底下全说懂了,我???,感觉全世界都会了堆,就我不会,于是我决定补一补: ——————来自百度百科 所 ...

  2. 【洛谷P1090 合并果子】

    题目描述 在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆.多多决定把所有的果子合成一堆. 每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和.可 ...

  3. 洛谷P1090 合并果子

    合并果子 题目链接 这个只能用于结构体中 struct item { int val; friend bool operator < (item a,item b) { return a.val ...

  4. [NOIP2004] 提高组 洛谷P1090 合并果子

    题目描述 在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆.多多决定把所有的果子合成一堆. 每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和.可 ...

  5. 洛谷 p1090 合并果子

    https://www.luogu.org/problemnew/show/P1090 优先队列的经典题目 体现了stl的优越性 #include<bits/stdc++.h> using ...

  6. 洛谷 P1090合并果子【贪心】【优先队列】

    题目描述 在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆.多多决定把所有的果子合成一堆. 每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和.可 ...

  7. 洛谷P1090 合并果子【贪心】

    在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆.多多决定把所有的果子合成一堆. 每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和.可以看出,所 ...

  8. Java实现 洛谷 P1090 合并果子

    import java.io.BufferedInputStream; import java.util.Arrays; import java.util.Scanner; public class ...

  9. 洛谷P1090——合并果子(贪心)

    https://www.luogu.org/problem/show?pid=1090 题目描述 在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆.多多决定把所有的果子合 ...

随机推荐

  1. 如何在 Spring Boot 中禁用 Actuator 端点安全性?

    默认情况下,所有敏感的 HTTP 端点都是安全的,只有具有 ACTUATOR 角色的用户才能访问它们.安全性是使用标准的 HttpServletRequest.isUserInRole 方法实施的. ...

  2. Error和Exception有什么区别?

    Error表示系统级的错误和程序不必处理的异常,是恢复不是不可能但很困难的情况下的一种严重问题:比如内存溢出,不可能指望程序能处理这样的情况:Exception表示需要捕捉或者需要程序进行处理的异常, ...

  3. JWT jti和kid属性的说明

    jti chaim=== JWT ID " jti"(JWT ID)声明为JWT提供了唯一的标识符. 标识符值的分配方式必须确保将相同值偶然分配给不同数据对象的可能性可以忽略不计: ...

  4. Collection和 Collections的区别?

    Collection是集合类的上级接口,继承与他的接口主要有Set和List.Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索.排序.线程安全化等操作.

  5. Java 中 notify 和 notifyAll 有什么区别?

    notify() 方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它才有 用武之地.而 notifyAll()唤醒所有线程并允许他们争夺锁确保了至少有一个线程 能继续运行.

  6. mysql问题排查与性能优化

     MySQL 问题排查都有哪些手段? 使用 show processlist 命令查看当前所有连接信息. 使用 explain 命令查询 SQL 语句执行计划. 开启慢查询日志,查看慢查询的 SQL. ...

  7. 学习Apache(五)

     apache目前主要有两种模式:prefork模式和worker模式: 1)prefork模式(默认模式) prefork是Unix平台上的默认(缺省)MPM,使用多个子进程,每个子进程只有一个线程 ...

  8. 【静态页面架构】CSS之定位

    CSS架构 1.浮动: 是以float属性设置容器指定的位置 <style> div { width: 200px; height: 200px; } #qq { background-c ...

  9. ES6-11学习笔记--箭头函数

    1.this指向定义时所在的对象,而不是调用时所在的对象 2.不可以当做构造函数 3.不可以使用arguments对象   ES5中定义函数的两种方式: function fn1() { consol ...

  10. Hadoop搭建高可用的HA集群

    一.工具准备 1.7台虚拟机(至少需要3台),本次搭建以7台为例,配好ip,关闭防火墙,修改主机名和IP的映射关系(/etc/hosts),关闭防火墙 2.安装JDK,配置环境变量 二.集群规划: 集 ...