凌乱的yyy / 线段覆盖
题目背景
快 noip 了,yyy 很紧张!
题目描述
现在各大 oj 上有 n 个比赛,每个比赛的开始、结束的时间点是知道的。
yyy 认为,参加越多的比赛,noip 就能考的越好(假的)。
所以,他想知道他最多能参加几个比赛。
由于 yyy 是蒟蒻,如果要参加一个比赛必须善始善终,而且不能同时参加 2 个及以上的比赛。
输入格式
第一行是一个整数 n ,接下来 n行每行是 2 个整数 ai,bi,表示比赛开始、结束的时间。
输出格式
一个整数最多参加的比赛数目。
输入输出样例
输入 #1复制
3
0 2
2 4
1 3
输出 #1复制
2
说明/提示
对于20% 的数据, n≤10。
对于 50%50% 的数据, 10^3 。
对于100% 的数据, 0≤ai<bi ≤10^6 。
思路:贪心,很经典的贪心算法。
可以看做:
在一个数轴上有n条线段,现要选取其中k条线段使得这k条线段两两没有重合部分,问最大的k为多少。
最左边的线段放什么最好?
显然放右端点最靠左的线段最好,从左向右放,右端点越小妨碍越少
其他线段放置按右端点排序,贪心放置线段,即能放就放
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
public class Main {
static int n;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
Node[] nodes = new Node[n];
for(int i=0;i<n;i++){
nodes[i] = new Node(sc.nextInt(),sc.nextInt());
}
Arrays.sort(nodes);
int ans = 0;
int time = -1;
for(int i=0;i<n;i++){
if(nodes[i].begin>=time){
ans++;
time=nodes[i].end;
}
}
System.out.print(ans);
}
}
class Node implements Comparable<Node> {
int begin,end;
public Node() {
}
public Node(int begin, int end) {
this.begin = begin;
this.end = end;
}
@Override
public int compareTo(Node o) {
return this.end-o.end;
}
}
删除问题
题目描述
键盘输入一个高精度的正整数N(不超过250位) ,去掉其中任意k个数字后剩下的数字按原左右次序将组成一个新的正整数。编程对给定的N和k,寻找一种方案使得剩下的数字组成的新数最小。
输入格式
n (高精度的正整数)
k(需要删除的数字个数)
输出格式
最后剩下的最小数。
输入输出样例
输入 #1复制
175438
4
输出 #1复制
13
思路:每次比较相邻两位,如果前一位大于后一位就把前一位删掉,每删除一次,便从头开始重新遍历。注意123456这种情况,如果遍历了一边一个数都没删掉,那么就删最后一位。注意在输出的时候,前导0不必输出。
import java.util.*;
public class Main {
static char[] n ;
static int k,count;
static ArrayList<Integer> arrayList ;
public static void main(String[] args) {
arrayList = new ArrayList<>();
Scanner sc = new Scanner(System.in);
n = sc.next().toCharArray();
k = sc.nextInt();
for(char ch: n){
int temp = ch-'0';
arrayList.add(temp);
}
while(true){
boolean flag = false;
for(int i=0;i<arrayList.size()-1;i++){
if(arrayList.get(i)>arrayList.get(i+1)&&arrayList.get(i)!=0){
Integer temp = arrayList.get(i);
arrayList.remove(temp);
count++;
flag = true;
break;
}
}
if(count==k){
break;
}
if(!flag){
Integer temp = arrayList.get(arrayList.size()-1);
arrayList.remove(temp);
count++;
}
}
boolean flag = false;
for(int i=0;i<arrayList.size();i++){
if(!flag&&arrayList.get(i)==0){
continue;
}else{
flag = true;
System.out.print(arrayList.get(i));
}
}
if(!flag){
System.out.print("0");
}
}
}
铺设道路
题目描述
春春是一名道路工程师,负责铺设一条长度为 n 的道路。
铺设道路的主要工作是填平下陷的地表。整段道路可以看作是 n 块首尾相连的区域,一开始,第 i 块区域下陷的深度为 di。
春春每天可以选择一段连续区间[L,R] ,填充这段区间中的每块区域,让其下陷深度减少 1。在选择区间时,需要保证,区间内的每块区域在填充前下陷深度均不为 0 。
春春希望你能帮他设计一种方案,可以在最短的时间内将整段道路的下陷深度都变为 0 。
输入格式
输入文件包含两行,第一行包含一个整数 n,表示道路的长度。 第二行包含 n 个整数,相邻两数间用一个空格隔开,第i 个整数为 di 。
输出格式
输出文件仅包含一个整数,即最少需要多少天才能完成任务。
输入输出样例
输入 #1复制
6
4 3 2 5 3 5
输出 #1复制
9
说明/提示
【样例解释】
一种可行的最佳方案是,依次选择: [1,6]、[1,6]、[1,2]、[1,1]、[4,6]、[4,4]、[4,4]、[6,6]、[6,6]。
【数据规模与约定】
对于 30% 的数据,1≤n≤10 ;
对于 70% 的数据,1≤n≤1000 ;
对于 100% 的数据,1≤n≤100000,0≤di ≤10000 。
思路:递推。。。
用f[i]表示前i个坑所铺设的最少天数
那么要做的只需比较一下当前的a[i](就是坑的深度)和a[i-1],分两种情况:
如果a[i]<=a[i−1],那么在填a[i−1]时就可以顺便把a[i]填上,这样显然更优,所以f[i]=f[i-1];
否则的话,那么在填a[i−1]时肯定要尽量把a[i]一块填上,a[i]剩余的就单独填。。
所以,f[i]=f[i−1]+(a[i]−a[i−1])。
初始化f[1]=a[1],向后推就行了。
import java.util.*;
public class Main {
static int n;
static int[] d,ans;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
d = new int[n+1];
ans = new int[n+1];
for(int i=1;i<=n;i++){
d[i] = sc.nextInt();
}
ans[1] = d[1];
for(int i=2;i<=n;i++){
if(d[i]>d[i-1]){
ans[i] = ans[i-1] + d[i]-d[i-1];
}else {
ans[i] = ans[i-1];
}
}
System.out.print(ans[n]);
}
}
[AHOI2018初中组]分组
题目描述
小可可的学校信息组总共有n 个队员,每个人都有一个实力值a[i]。现在,一年一度的编程大赛就要到了,小可可的学校获得了若干个参赛名额,教练决定把学校信息组的n 个队员分成若干个小组去参加这场比赛。
但是每个队员都不会愿意与实力跟自己过于悬殊的队员组队,于是要求分成的每个小组的队员实力值连续,同时,一个队不需要两个实力相同的选手。举个例子:[1, 2, 3, 4, 5]是合法的分组方案,因为实力值连续;[1, 2, 3, 5]不是合法的分组方案,因为实力值不连续;[0, 1, 1, 2]同样不是合法的分组方案,因为出现了两个实力值为1 的选手。
如果有小组内人数太少,就会因为时间不够而无法获得高分,于是小可可想让你给出一个合法的分组方案,满足所有人都恰好分到一个小组,使得人数最少的组人数最多,输出人数最少的组人数的最大值。
注意:实力值可能是负数,分组的数量没有限制。
输入格式
输入有两行:
第一行一个正整数n,表示队员数量。
第二行有n 个整数,第i 个整数a[i]表示第i 个队员的实力。
输出格式
输出一行,包括一个正整数,表示人数最少的组的人数最大值。
输入输出样例
输入 #1复制
7
4 5 2 3 -4 -3 -5
输出 #1复制
3
说明/提示
【样例解释】 分为2 组,一组的队员实力值是4,5,2,3,一组是−4,−3,−5,其中最小的组人数为3,可以发现没有比3 更优的分法了。
【数据范围】
对于100%的数据满足:1≤n≤100000,a[i]≤10^9。
思路:栈+贪心。
先将数据进行排序,从头到尾依次遍历数组元素。如果发现已经开辟的栈中最后一个元素符合stacks[j].peek== a[i]-1 那么便将a[i]入栈,如果不存在这样的栈,那么便再开辟一个新的栈,并将a[i]入栈。
如果有多个栈符合stacks[j].peek==a[i]-1怎么办呢?我们需要将a[i]加入到最后一个符合条件的栈中。因为他开辟的晚,元素少,为他添加元素,可以提高最少人数组的平均值。
import java.util.Arrays;
import java.util.Collections;
import java.util.Scanner;
import java.util.Stack;
public class Main {
static int[] a;
static Stack[] stacks = new Stack[100005];
static int n,min=99999999,count;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
a = new int[n];
for(int i=0;i<n;i++){
a[i] = sc.nextInt();
}
Arrays.sort(a);
for(int i=0;i<n;i++){
int temp = -1;
for(int j=0;j<count;j++){
if((a[i]-1)==(int)stacks[j].peek()){
temp = j;
}
}
if(temp!=-1){
stacks[temp].push(a[i]);
}else {
stacks[count]=new Stack();
stacks[count].push(a[i]);
count++;
}
}
for(int i=0;i<count;i++){
min = Math.min(min,stacks[i].size());
}
System.out.println(min);
}
}
跳跳!
题目描述
你是一只小跳蛙,你特别擅长在各种地方跳来跳去。
这一天,你和朋友小 F 一起出去玩耍的时候,遇到了一堆高矮不同的石头,其中第 ii 块的石头高度为hi,地面的高度是h0=0。你估计着,从第 i块石头跳到第 j 块石头上耗费的体力值为 (hi - hj) ^ 2,从地面跳到第 i 块石头耗费的体力值是 (hi) ^ 2 。
为了给小 F 展现你超级跳的本领,你决定跳到每个石头上各一次,并最终停在任意一块石头上,并且小跳蛙想耗费尽可能多的体力值。
当然,你只是一只小跳蛙,你只会跳,不知道怎么跳才能让本领更充分地展现。
不过你有救啦!小 F 给你递来了一个写着 AK 的电脑,你可以使用计算机程序帮你解决这个问题,万能的计算机会告诉你怎么跳。
那就请你——会写代码的小跳蛙——写下这个程序,为你 NOIp AK 踏出坚实的一步吧!
输入格式
输入一行一个正整数 n,表示石头个数。
输入第二行 n 个正整数,表示第i块石头的高度 hi 。
输出格式
输出一行一个正整数,表示你可以耗费的体力值的最大值。
输入输出样例
输入 #1复制
2
2 1
输出 #1复制
5
输入 #2复制
3
6 3 5
输出 #2复制
49
思路:将数据进行排序,每次在没有跳过的最大最小值之间跳,此时差的绝对值最大,结果最大。注意要用long类型储存结果。。。
import java.util.Arrays;
import java.util.Scanner;
public class Main {
static int n;
static long ans;
static int[] h;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
h = new int[n];
for(int i=0;i<n;i++){
h[i] = sc.nextInt();
}
Arrays.sort(h);
int index1 = n-1;
int index2 = 0;
ans += Math.pow(h[n-1],2);
while(index1>index2){
ans+=Math.pow((h[index1]-h[index2]),2);
index1--;
if(index1==index2){
break;
}
ans+=Math.pow((h[index1]-h[index2]),2);
index2++;
}
System.out.println(ans);
}
}
合并果子
题目描述
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n−1 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 1 ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有 3 种果子,数目依次为 1 , 2 , 9 。可以先将 1 、 2 堆合并,新堆数目为3 ,耗费体力为 3 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12 ,耗费体力为 12 。所以多多总共耗费体力 =3+12=15 。可以证明 15 为最小的体力耗费值。
输入格式
共两行。
第一行是一个整数 n(1≤n≤10000) ,表示果子的种类数。
第二行包含 n 个整数,用空格分隔,第 i 个整数 )ai (1≤ai≤20000) 是第 i 种果子的数目。
输出格式
一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 2^31
输入输出样例
输入 #1复制
3
1 2 9
输出 #1复制
15
说明/提示
对于30%的数据,保证有n≤1000:
对于50%的数据,保证有n≤5000;
对于全部的数据,保证有n≤10000。
思路:贪心。将数据储存在集合中,每次按照升序进行排序,弹出前两个元素进行合并,记录消耗的体力,将合并后的数据储存进集合,并将弹出的两个元素删掉,循环n-1次即可。
import java.util.*;
public class Main {
static int n,sum;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
ArrayList<Integer> arrayList = new ArrayList<>();
for(int i=0;i<n;i++){
arrayList.add(sc.nextInt());
}
for(int i=0;i<n-1;i++){
Collections.sort(arrayList);
int t1 = arrayList.get(0);
int t2 = arrayList.get(1);
sum+=t1+t2;
arrayList.add(t1+t2);
arrayList.remove(new Integer(t1));
arrayList.remove(new Integer(t2));
}
System.out.print(sum);
}
}
小A的糖果
题目描述
小 A 有 n个糖果盒,第 i 个盒中有 ai颗糖果。
小 A 每次可以从其中一盒糖果中吃掉一颗,他想知道,要让任意两个相邻的盒子中糖的个数之和都不大于 x,至少得吃掉几颗糖。
输入格式
输入的第一行是两个用空格隔开的整数,代表糖果盒的个数 n 和给定的参数 x。
第二行有 n 个用空格隔开的整数,第 i 个整数代表第 i 盒糖的糖果个数 ai 。
输出格式
输出一行一个整数,代表最少要吃掉的糖果的数量。
输入输出样例
输入 #1复制
3 3
2 2 2
输出 #1复制
1
输入 #2复制
6 1
1 6 1 2 0 4
输出 #2复制
11
输入 #3复制
5 9
3 1 4 1 5
输出 #3复制
0
思路:每次判断两个数据的和是否大于了x,如果大于了x,则需要吃掉糖果,此时要先在右边这组数据上减去。
import java.util.*;
public class Main {
static long n,x,sum;
static int[] arr;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextLong();
x = sc.nextLong();
arr = new int[(int)n];
for(int i=0;i<n;i++){
arr[i] = sc.nextInt();
}
for(int i=1;i<n;i++){
if(arr[i]+arr[i-1]>x){
long temp =arr[i]+arr[i-1]-x;
sum += temp;
if(arr[i]>=temp){
arr[i] -= temp;
}else{
arr[i] = 0;
}
}
}
System.out.println(sum);
}
}
【深基12.例1】部分背包问题
题目描述
阿里巴巴走进了装满宝藏的藏宝洞。藏宝洞里面有 N(N≤100) 堆金币,第 i 堆金币的总重量和总价值分别是)mi ,vi (1≤mi ,vi≤100)。阿里巴巴有一个承重量T(T≤1000) 的背包,但并没办法将全部的金币都装进去。他想装走尽可能多价值的金币。所有金币都可以随意分割,分割完的金币重量价值比(也就是单位价格)不变。请问阿里巴巴最多可以拿走多少价值的金币?
输入格式
第一行两个整数 N、T
接下来 N 行,每行两个整数 mi ,vi
输出格式
一个整数表示答案,输出两位小数
输入输出样例
输入 #1复制
4 50
10 60
20 100
30 120
15 45
输出 #1复制
240.00
思路:题目叫背包问题,其实是一道贪心题。每次取性价比最高的就好了。
import java.util.Arrays;
import java.util.Scanner;
public class Main {
static int n,t,index;
static double ans;
static double v,m,d;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
t = sc.nextInt();
Node[] node = new Node[n];
for(int i=0;i<n;i++){
m = sc.nextDouble();
v = sc.nextDouble();
d = v/m;
node[i] = new Node(m,v,d);
}
Arrays.sort(node);
while(t>0&&index<n){
if(node[index].m<=t){
ans += node[index].v;
t -=node[index].m;
index++;
}else{
ans+=(node[index].d*t);
t = 0;
node[index].m-=t;
}
}
System.out.printf("%.2f",ans);
}
}
class Node implements Comparable<Node>{
double m,v,d;
public Node() {
}
public Node(double m, double v, double d) {
this.m = m;
this.v = v;
this.d = d;
}
@Override
public int compareTo(Node o) {
return o.d>this.d?1:-1;
}
}
陶陶摘苹果(升级版)
题目描述
又是一年秋季时,陶陶家的苹果树结了 n 个果子。陶陶又跑去摘苹果,这次他有一个 a 公分的椅子。当他手够不着时,他会站到椅子上再试试。
这次与 NOIp2005 普及组第一题不同的是:陶陶之前搬凳子,力气只剩下 s 了。当然,每次摘苹果时都要用一定的力气。陶陶想知道在 s<0 之前最多能摘到多少个苹果。
现在已知 n 个苹果到达地上的高度 xi ,椅子的高度 a,陶陶手伸直的最大长度 b,陶陶所剩的力气 s,陶陶摘一个苹果需要的力气yi ,求陶陶最多能摘到多少个苹果。
输入格式
第 1 行:两个数 苹果数 n,力气 s。
第 2 行:两个数 椅子的高度 a,陶陶手伸直的最大长度 b。
第 3 行~第 3+n−1 行:每行两个数 苹果高度xi ,摘这个苹果需要的力气 yi 。
输出格式
只有一个整数,表示陶陶最多能摘到的苹果数。
输入输出样例
输入 #1复制
8 15
20 130
120 3
150 2
110 7
180 1
50 8
200 0
140 3
120 2
输出 #1复制
4
思路:先按照高度排序,把摘不到的筛掉,再按照力气排序,直到s<=0为止。
import java.util.*;
public class Main {
static int n,s,a,b;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
s = sc.nextInt();
a = sc.nextInt();
b = sc.nextInt();
Node[] node = new Node[n];
for(int i=0;i<n;i++){
node[i] = new Node();
node[i].x = sc.nextInt();
node[i].y = sc.nextInt();
}
Arrays.sort(node,new bx());
int index = n;
for(int i=0;i<n;i++){
if(node[i].x>(a+b)){
index = i;
break;
}
}
n = index;
Arrays.sort(node,0,n,new by());
int ans = 0;
for(int i=0;i<=n;i++){
if(node[i].y>s){
break;
}else{
ans++;
s-=node[i].y;
}
}
System.out.print(ans);
}
}
class Node{
int x,y;
}
class bx implements Comparator<Node>{
@Override
public int compare(Node o1, Node o2) {
return o1.x-o2.x;
}
}
class by implements Comparator<Node>{
@Override
public int compare(Node o1, Node o2) {
return o1.y-o2.y;
}
}
纪念品分组
题目描述
元旦快到了,校学生会让乐乐负责新年晚会的纪念品发放工作。为使得参加晚会的同学所获得 的纪念品价值相对均衡,他要把购来的纪念品根据价格进行分组,但每组最多只能包括两件纪念品, 并且每组纪念品的价格之和不能超过一个给定的整数。为了保证在尽量短的时间内发完所有纪念品,乐乐希望分组的数目最少。
你的任务是写一个程序,找出所有分组方案中分组数最少的一种,输出最少的分组数目。
输入格式
共 n+2 行:
第一行包括一个整数 w,为每组纪念品价格之和的上上限。
第二行为一个整数 n,表示购来的纪念品的总件数 G。
第 3∼n+2 行每行包含一个正整数 Pi表示所对应纪念品的价格。
输出格式
一个整数,即最少的分组数目。
输入输出样例
输入 #1复制
100
9
90
20
20
30
50
60
70
80
90
输出 #1复制
6
思路:将数据按照升序排序,从小到大遍历,对于每个p[i] ,我们从大到小寻找一个p[j],使p[i]+p[j]<=w,则这两组数据为一组。如果找不到j,那么第i组数据单独为一组。
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
public class Main {
static int w,n,ans;
static int[] p;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
w = sc.nextInt();
n = sc.nextInt();
p = new int[n];
for(int i=0;i<n;i++){
p[i] = sc.nextInt();
}
Arrays.sort(p);
for(int i=0;i<n;i++){
if(p[i]>0){
boolean flag = false;
for(int j=n-1;j>i;j--){
if(p[j]>0&&(p[j]+p[i])<=w){
ans++;
p[j]=-1;
flag = true;
break;
}
}
if(!flag){
p[i]=-1;
ans++;
}
}
}
System.out.print(ans);
}
}