(1)P2240 【深基12.例1】部分背包问题
看到题目以为是动态规划,然而是贪心。就是讲单价性价比高的金币排在前面,然后如果能拿光就拿走全部,不能拿光就尽可能多的拿走。很基础的贪心方法。
import java.util.Arrays;
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(),m=sc.nextInt();
peo[] p=new peo[n];
for(int i=0;i<n;i++) {
int x=sc.nextInt();
double y=sc.nextDouble();
p[i]=new peo(y/x,x,y);
}
Arrays.sort(p);
double sum=0;
for(int i=0;i<n;i++) {
if(m>=p[i].num) {
sum+=p[i].all;
m-=p[i].num;
}
else {
sum+=p[i].money*m;
break;
}
}
System.out.println(String.format("%.2f", sum));
}
}
class peo implements Comparable<peo>{
double money;
int num;
double all;
peo(double money,int num,double all){
this.money=money;this.num=num;this.all=all;
}
@Override
public int compareTo(peo o) {
return (int)((o.money-money)*100);
}
}
(2)P1223 排队接水
和上题一样的思路,接水时间长的放后面,那么肯定总等待时间短。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
int n=Integer.valueOf(br.readLine());
String[] s=br.readLine().split(" ");
person[] p=new person[n];
for(int i=0;i<n;i++) {
p[i]=new person(i+1,Integer.valueOf(s[i]));
}
double all=0;
Arrays.sort(p);
for(int i=0;i<n;i++) {
System.out.print(p[i].id+" ");
all+=(n-i-1)*p[i].time;
}
System.out.println();
all/=n;
System.out.println(String.format("%.2f",(double)Math.round(all*100)/100));
}
}
class person implements Comparable<person>{
int id;
int time;
person(int id,int time){
this.id=id;
this.time=time;
}
@Override
public int compareTo(person o) {
return time-o.time;
}
}
(3)P1803 凌乱的yyy / 线段覆盖
这道题贪心的思路有很多:按照开始时间来进行排序,按照活动的时间来排序,然而这些都是不正确。正确的贪心方法是,以结束时间早的活动来排序,因为我们认为,越早结束的活动能为后面安排的活动尽可能多。
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
node[] arr=new node[n];
for(int i=0;i<n;i++) {
arr[i]=new node(sc.nextInt(),sc.nextInt());
}
Arrays.sort(arr);
int sum=0,time=0;
for(int i=0;i<n;i++) {
if(arr[i].start>=time) {
sum++;
time=arr[i].end;
}
}
System.out.println(sum);
}
}
class node implements Comparable<node>{
int start,end;
node(int start,int end){
this.start=start;this.end=end;
}
@Override
public int compareTo(node o) {
return end-o.end;
}
}
(4)P1090 合并果子 / [USACO06NOV]Fence Repair G
这题需要用到一种数据结构,叫优先队列。它可以保证每次最小的数在最前面。而这道题,我们可以直到合并的次数是一定的,那么我们每次和并尽量选小的两队数目合并,就能得到合并最少体力。所以每次我们选择合并优先队列的前两个元素。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Queue;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
int n=Integer.valueOf(br.readLine());
int[] arr=new int[n];
String[] s=br.readLine().split(" ");
Queue<Integer> q=new PriorityQueue<Integer>();
for(int i=0;i<n;i++) {
q.add(Integer.valueOf(s[i]));
}
int sum=0;
int temp;
while(q.size()>1) {
temp=q.remove()+q.remove();
sum+=temp;
q.add(temp);
}
System.out.println(sum);
}
}
(5)P3817 小A的糖果
这道题正确的贪心方法应该是,尽可能使盒子的糖果数接近x,首先我们特判第一个盒子,如果第一个盒子大于糖果数,那么我们吃掉一部分,使得第一盒子剩x个,如果第一个盒子不大于x,那么不用管它。从第二个盒子开始,每次计算它和它前面一个盒子的总数,使得等于x,如果大于x则吃掉一部分使得这个盒子与它前面一个盒子总和为x。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String[] s=br.readLine().split(" ");
int n=Integer.valueOf(s[0]),m=Integer.valueOf(s[1]);
s=br.readLine().split(" ");
long sum,a;
if(Integer.valueOf(s[0])>m) {
sum=Integer.valueOf(s[0])-m;
a=m;
}
else {
sum=0;
a=Integer.valueOf(s[0]);
}
for(int i=1;i<n;i++) {
if(Integer.valueOf(s[i])+a>m) {
sum+=Integer.valueOf(s[i])+a-m;
a=m-a;
}
else {
a=Integer.valueOf(s[i]);
}
}
System.out.println(sum);
}
}
(6)P1106 删数问题
毕竟难的一道贪心题目。思路:我们希望从左开始在尽量大的范围内找到一个最小的数作为结果的第一位,但范围不能超过m+1。然后把这个最小的数字左边都删了,如果有多个最小取左边那个。然后从新的序列里面找第二位最小的数,直到k个数用完了。注意前导0的情况。
(7)P1478 陶陶摘苹果(升级版)
首先判断有哪些苹果是摘不到的,然后在剩下能摘到的苹果当中,按照力气从小到大排序。一直贪心的选择需要花力气小的苹果。
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(),s=sc.nextInt(),high=sc.nextInt()+sc.nextInt();
int[] arr=new int[n];
for(int i=0;i<n;i++) {
if(sc.nextInt()<=high) {
arr[i]=sc.nextInt();
}
else {
sc.nextInt();
arr[i]=-1;
}
}
Arrays.sort(arr);
int sum=0;
for(int i=0;i<n;i++) {
if(arr[i]==-1) {
continue;
}
if(s>=arr[i]) {
sum++;
s-=arr[i];
}
else {
break;
}
}
System.out.println(sum);
}
}
(8)P5019 铺设道路
这道题贪心的思想是小的坑会被大的坑填掉。我们先设第一个坑为基准 ,在后面,如果小的坑会被顺带填掉,大的坑会减少a[i]-a[i-1]的深度。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int[] arr=new int[n];
for(int i=0;i<n;i++)
arr[i]=sc.nextInt();
int sum=arr[0];
for(int i=1;i<n;i++) {
if(arr[i]>arr[i-1]) {
sum+=arr[i]-arr[i-1];
}
}
System.out.println(sum);
}
}
(9)P1208 [USACO1.3]混合牛奶 Mixing Milk
这道题和第一次是思路是一样的,都是找单价最便宜的。然后对于最后一个不用全买,还差多少买多少。
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt(),m=sc.nextInt();
pp[] arr=new pp[m];
for(int i=0;i<m;i++) {
arr[i]=new pp(sc.nextInt(),sc.nextInt());
}
Arrays.sort(arr);
int sum=0;
for(int i=0;i<n;i++) {
if(n>arr[i].y) {
sum+=arr[i].x*arr[i].y;
n-=arr[i].y;
}
else {
sum+=arr[i].x*n;
break;
}
}
System.out.println(sum);
}
}
class pp implements Comparable<pp>{
int x,y;
pp(int xx,int yy){
x=xx;y=yy;
}
@Override
public int compareTo(pp o) {
return x-o.x;
}
}
(10)P1094 纪念品分组
将纪念品的价格从小到大排序。然后设立两个指针,一个从开始,一个从末尾开始,用头指针最小的数的这个加上末尾指针也就是最大的这个数,如果他们小于等于x,那么两边指针都移动,如果大于了x,说明最大的那个数字只能单独作为一个分组。
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int x=sc.nextInt(),n=sc.nextInt();
int[] arr=new int[n];
for(int i=0;i<n;i++) {
arr[i]=sc.nextInt();
}
Arrays.sort(arr);
int p=0,q=n-1;
int sum=0;
while(p<=q) {
if(arr[p]+arr[q]<=x) {
p++;q--;
}
else {
q--;
}
sum++;
}
System.out.println(sum);
}
}
(11)P4995 跳跳!
和前面一题有些类似,将石头从低到高排序,然后要尽可能跳多的高度,那么肯定是从最高到最低,然后从最低到最高这样往返着跳,直到所有石头都被跳过了。
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int[] arr=new int[n+1];
for(int i=1;i<=n;i++) {
arr[i]=sc.nextInt();
}
Arrays.sort(arr);
int p=0,q=n;
boolean flag=true;
long sum=0;
while(p<q) {
sum+=(arr[q]-arr[p])*(arr[q]-arr[p]);
if(flag) {
p++;
}
else {
q--;
}
flag=!flag;
}
System.out.println(sum);
}
}
(13)P1080 国王游戏
一道省选难度的题目,代码不难实现,但是思路非常难想。贪心的方法是left1*right1<left2 *right2。也就是说,左手右手乘积小的金币数放前面。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.util.Arrays;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
int n=Integer.valueOf(br.readLine());
String[] s=br.readLine().split(" ");
int left=Integer.valueOf(s[0]),right=Integer.valueOf(s[1]);
pp[] p=new pp[n];
for(int i=0;i<n;i++) {
s=br.readLine().split(" ");
p[i]=new pp(Integer.valueOf(s[0]),Integer.valueOf(s[1]));
}
Arrays.sort(p);
BigInteger max=BigInteger.ZERO;
BigInteger sum=new BigInteger(left+"");
BigInteger pleft;
BigInteger pright;
for(int i=0;i<n;i++) {
pleft=new BigInteger(p[i].left+"");
pright=new BigInteger(p[i].right+"");
if(max.compareTo(sum.divide(pright))<0) {
max=sum.divide(pright);
}
sum=sum.multiply(pleft);
}
System.out.println(max);
}
}
class pp implements Comparable<pp>{
int left;
int right;
pp(int l,int r){
left=l;
right=r;
}
@Override
public int compareTo(pp o) {
return left*right-o.left*o.right;
}
}