125.耍杂技的牛
https://www.acwing.com/solution/acwing/content/845/
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
List list = new ArrayList();
int n = scan.nextInt();
for(int i = 0;i < n;i++)
{
int w = scan.nextInt();
int s = scan.nextInt();
list.add(new PIIs(w + s,w));
}
Collections.sort(list);
int res = Integer.MIN_VALUE;
int sum = 0;//记录危险值
for(PIIs item : list)
{
int w = item.getSecond();
int s = item.getFirst() - w;
res = Math.max(res, sum - s);
sum += w;
}
System.out.println(res);
}
}
class PIIs implements Comparable{
private int first;
private int second;
public int getFirst()
{
return this.first;
}
public int getSecond()
{
return this.second;
}
public PIIs(int first,int second)
{
this.first = first;
this.second = second;
}
@Override
public int compareTo(PIIs o) {
// TODO 自动生成的方法存根
return Integer.compare(first, o.first);
}
}
905.区间选点
题目描述:
给定N个闭区间[ai,bi],请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。
输出选择的点的最小数量。
位于区间端点上的点也算作区间内。
算法分析
1、将每个区间按右端点从小到大进行排序
-
2、从前往后枚举每个区间,初始选定end值为无穷小
- 若当前区间中包含该点end,则直接跳过
- 否则,选择当前区间的右端点
-
证明:
- (1)找到
cnt
个点,满足题意情况,则最优解Ans <= cnt
- (2)找到
cnt
个点,即找到cnt
个区间,且区间从左到右依次排好,且没有相同的交集,则说明可能有区间没有被这cnt
个点覆盖过,所以最优解Ans >= cnt
- 则
Ans == cnt
,证毕
- (1)找到
时间复杂度
Java 代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
List list = new ArrayList();
int n = scan.nextInt();
for(int i = 0;i < n;i++)
{
int L = scan.nextInt();
int R = scan.nextInt();
list.add(new PIIs(L,R));
}
//按右端点进行排序
Collections.sort(list);
int count = 0;
int end = Integer.MIN_VALUE;
for(PIIs item:list)
{
if(item.getFirst() > end)
{
count ++;
end = item.getSecond();
}
}
System.out.println(count);
}
}
class PIIs implements Comparable{
private int first;
private int second;
public int getFirst()
{
return this.first;
}
public int getSecond()
{
return this.second;
}
public PIIs(int first,int second)
{
this.first = first;
this.second = second;
}
@Override
public int compareTo(PIIs o) {
// TODO 自动生成的方法存根
return Integer.compare(second, o.second);
}
}
906. 区间分组
题目描述:
给定N个闭区间[ai,bi],请你将这些区间分成若干组,使得每组内部的区间两两之间(包括端点)没有交集,并使得组数尽可能小。
输出最小组数。
算法分析
- 1、将所有区间按左端点从小到大排序
-
2、从前往后枚举每个区间,判断能否将其放到某个现有的组中,即是否存在区间的的左端点L > 所有组中右端点的最小值
- 如果不存在这样的组,则开新组,然后再将其放进组中
- 如果存在这样的组,则将其放在符合条件的组中,并更新当前组的右端点的值
- 3、为了不用每次选择组时都遍历所有组,可以通过小根堆来维护所有组中的尾端
证明:
- 1、按照上述存放,一定是一种合法的方案,则
Ans <= cnt
- 2、由于分了
cnt
个组,则说明一定存在cnt个区间含有公共点,则一定至少开cnt
个组,则Ans >= cnt
综合1、2可推出Ans == cnt
时间复杂度
Java 代码
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
List list = new ArrayList();
int n = scan.nextInt();
for(int i = 0;i < n;i++)
{
int L = scan.nextInt();
int R = scan.nextInt();
list.add(new PIIs(L,R));
}
//按左端点排序
Collections.sort(list);
//通过小根堆维护每组的尾端
Queue minheap = new PriorityQueue();
for(PIIs item : list)
{
if(minheap.isEmpty() || minheap.peek() >= item.getFirst())
minheap.add(item.getSecond());
else
{
//更新小根堆
minheap.poll();
minheap.add(item.getSecond());
}
}
System.out.println(minheap.size());
}
}
class PIIs implements Comparable{
private int first;
private int second;
public int getFirst()
{
return this.first;
}
public int getSecond()
{
return this.second;
}
public PIIs(int first,int second)
{
this.first = first;
this.second = second;
}
@Override
public int compareTo(PIIs o) {
// TODO 自动生成的方法存根
return Integer.compare(first, o.first);
}
}
803.区间合并
给定 n 个区间 [li,ri],要求合并所有有交集的区间。
注意如果在端点处相交,也算有交集。
输出合并完成后的区间个数。
算法分析
- 1、将每个区间按左端点从小到大进行排序
- 2、如图所示,可分3种情况
- 情况一:当前区间完全被上一区间覆盖,直接跳过
- 情况二:将当前区间的右端点更新为上一区间的右端点,达到区间延长的效果
- 情况三:当前区间的左端点严格大于上一区间的右端点,则表示该区间不能合并,更新区间且count++
时间复杂度
Java 代码
import java.util.*;
public class Main{
public static void main(String[] args) {
List list = new ArrayList();
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
for(int i = 0;i < n;i++)
{
int L = scan.nextInt();
int R = scan.nextInt();
list.add(new PIIs(L,R));
}
//按左端点进行排序
Collections.sort(list);
int count = 0;
int start = Integer.MIN_VALUE;
int end = Integer.MIN_VALUE;
for(PIIs item : list)
{
if(item.getFirst() > end)
{
count ++;
start = item.getFirst();
end = item.getSecond();
}
else
{
end = Math.max(end, item.getSecond());
}
}
System.out.println(count);
}
}
class PIIs implements Comparable{
private int first;
private int second;
public int getFirst()
{
return this.first;
}
public int getSecond()
{
return this.second;
}
public PIIs(int first,int second)
{
this.first = first;
this.second = second;
}
@Override
public int compareTo(PIIs o) {
// TODO 自动生成的方法存根
return Integer.compare(first, o.first);
}
}
148. 合并果子
题目描述:
在一个果园里,达达已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。
达达决定把所有的果子合成一堆。
每一次合并,达达可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。
可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。
达达在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以达达在合并果子时要尽可能地节省体力。
假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使达达耗费的体力最少,并输出这个最小的体力耗费值。
例如有3种果子,数目依次为1,2,9。
可以先将1、2堆合并,新堆数目为3,耗费体力为3。
接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。
所以达达总共耗费体力=3+12=15。
可以证明15为最小的体力耗费值。
算法分析
- 典型的构造哈夫曼树的问题,只需每次找出最小的两个值合并成一个元素,该做法消耗体力总和最低
时间复杂度
Java 代码
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
Queue minheap = new PriorityQueue();
int n = scan.nextInt();
for(int i = 0;i < n;i++)
{
minheap.add(scan.nextInt());
}
int res = 0;
while(minheap.size() >= 2)
{
int x = minheap.poll();
int y = minheap.poll();
int value = x + y;
res += value;
minheap.add(value);
}
System.out.println(res);
}
}
907. 区间覆盖
题目描述:
给定N个闭区间[ai,bi]以及一个线段区间[s,t],请你选择尽量少的区间,将指定线段区间完全覆盖。
输出最少区间数,如果无法完全覆盖则输出-1。
算法分析
- 1、将所有区间按左端点从小到大进行排序
- 2、从前往后枚举每个区间,在所有能覆盖start的区间中,选择右端点最大的区间,然后将start更新成右端点的最大值
证明
- 在剩下所有能覆盖start的区间中,选择右端点最大的区间,则一定会比前面的选择最优,更快达到end,所以该做法一定是最优
时间复杂度
Java 代码
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
List list = new ArrayList();
int start = scan.nextInt();
int end = scan.nextInt();
int n = scan.nextInt();
for(int i = 0;i < n;i++)
{
int L = scan.nextInt();
int R = scan.nextInt();
list.add(new PIIs(L,R));
}
//按左端点排序
Collections.sort(list);
int res = 0;
for(int i = 0;i < n;i++)
{
int j = i;
int r = Integer.MIN_VALUE;//标识是否可以完全覆盖
while(j < n && list.get(j).getFirst() <= start)
{
r = Math.max(r, list.get(j).getSecond());
j ++;
}
//若不能完全覆盖
if(r < start)
{
res = -1;
break;
}
res ++;
if(r >= end)
{
break;
}
start = r;
i = j - 1;
}
System.out.println(res);
}
}
class PIIs implements Comparable{
private int first;
private int second;
public int getFirst()
{
return this.first;
}
public int getSecond()
{
return this.second;
}
public PIIs(int first,int second)
{
this.first = first;
this.second = second;
}
@Override
public int compareTo(PIIs o) {
// TODO 自动生成的方法存根
return Integer.compare(first, o.first);
}
}
908. 最大不相交区间数量
题目描述:
给定N个闭区间[ai,bi],请你在数轴上选择若干区间,使得选中的区间之间互不相交(包括端点)。
输出可选取区间的最大数量。
算法分析
(与AC905一样)https://www.acwing.com/solution/AcWing/content/5887/
1、将每个区间按右端点从小到大进行排序
-
2、从前往后枚举每个区间,初始选定end值为无穷小
- 若当前区间中包含该点end,则直接跳过
- 否则,选择当前区间的右端点
-
证明:
- (1)找到
cnt
个点,满足题意情况,则最优解Ans <= cnt
- (2)找到
cnt
个点,即找到cnt
个区间,且区间从左到右依次排好,且没有相同的交集,则说明可能有区间没有被这cnt
个点覆盖过,所以最优解Ans >= cnt
- 则
Ans == cnt
,证毕
- (1)找到
时间复杂度
Java 代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
List list = new ArrayList();
int n = scan.nextInt();
for(int i = 0;i < n;i++)
{
int L = scan.nextInt();
int R = scan.nextInt();
list.add(new PIIs(L,R));
}
//按右端点进行排序
Collections.sort(list);
int count = 0;
int end = Integer.MIN_VALUE;
for(PIIs item:list)
{
if(item.getFirst() > end)
{
count ++;
end = item.getSecond();
}
}
System.out.println(count);
}
}
class PIIs implements Comparable{
private int first;
private int second;
public int getFirst()
{
return this.first;
}
public int getSecond()
{
return this.second;
}
public PIIs(int first,int second)
{
this.first = first;
this.second = second;
}
@Override
public int compareTo(PIIs o) {
// TODO 自动生成的方法存根
return Integer.compare(second, o.second);
}
}
913. 排队打水
题目描述:
有 n 个人排队到 1 个水龙头处打水,第 i 个人装满水桶所需的时间是 ti,请问如何安排他们的打水顺序才能使所有人的等待时间之和最小?
算法分析
- 安排他们的打水顺序才能使所有人的等待时间之和最小,则需要将打水时间最短的人先打水
证明:
不妨设
- (1) ≠ ≠ ≠ ... ≠
- (2)~属于
- (3) < < <... < ,
1、由i
的任意性,打水的时间总和为 * (n - 1) + * (n - 2) + ... + * (n - n)
=n * ( + +... + ) - ( * 1 + * 2 + ... + * n)
2、即相当于求 * 1 + * 2 + ... + * n 的最大值
3、假设 , ,... , 是按自然顺序排好序时是最大值,即 = * 1 + * 2 + ... +
4、任意选择两项,,且 < ,c > 0,交换,位置得到, ,同时交换后不会对其他项造成影响
由于 * x + * (x + c) = * x + * x + * c > * x + * x + * c = * x + * (x + c),即交换之后比原来的值更小.由于选取的任意性可得假设成立.
时间复杂度
Java 代码
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int[] a = new int[n + 1];
for(int i = 1;i <= n;i++)
{
a[i] = scan.nextInt();
}
Arrays.sort(a);
long res = 0;
for(int i = 1;i <= n;i++)
{
res += (a[i] * (n - i));
}
System.out.println(res);
}
}