第八章(上) 贪心策略与动态规划

动态规划和贪心算法都属于递推算法,但是与dfs求解的个数或者是全部解不同的是,他们俩是用来求最优解,且都是用局部最优来推导全局最优解,是对遍历解空间的一种优化。当问题具有最优子结构时,可用动归,而贪心是动归的特例。贪心只不过是只要顾及眼前的最优就可以求得全部的最优,而动归不是。

1、

硬币问题

有1元,5元,10元,50元,100元,500元的硬币各c1,c5,c10,c50,c100,c500枚.

现在要用这些硬币来支付A元,最少需要多少枚硬币?

假定本题至少存在一种支付方案.

0≤ci≤10^9

0≤A≤10^9

输入:

第一行有六个数字,分别代表从小到大6种面值的硬币的个数

第二行为A,代表需支付的A元

样例:

输入

3 2 1 3 0 2
620

输出

6
*/

import java.util.Scanner;

class Main{
    static int[] cnts = new int[6];
    static int[] coins = {1, 5, 10, 50, 100, 500};
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        for (int i = 0; i < 6; i++) {
            cnts[i] = sc.nextInt();
        }
        int A = sc.nextInt();
        int res = f(A, 5);
        System.out.println(res);
    }
    static int f(int A,int cur){
        if(A<=0) return 0;
        int coinvalue =  coins[cur];
        int x =  A/coinvalue;
        int cnt  = cnts[cur];
        int t =  Math.min(x,cnt);
        return t+f(A-t*coinvalue,cur-1);
    }
}

2、http://poj.org/problem?id=1700

一群N人希望过一条只有一条船的河,最多可以载两个人。因此,必须安排某种穿梭布置,以便来回划船,以便所有人都可以穿越。每个人都有不同的划船速度; 一对夫妇的速度取决于较慢的速度。您的工作是确定一种策略,以最大限度地缩短这些人的时间。

输入的第一行包含单个整数T(1 <= T <= 20),即测试用例的数量。然后是T案例。每个案例的第一行包含N,第二行包含N个整数,给每个人过河的时间。每个案例前面都有一个空行。人数不会超过1000人,没有人需要超过100秒才能完成。

对于每个测试用例,打印一行,其中包含所有N人过河所需的总秒数。

样本输入

1
4
1 2 5 10

样本输出

17
import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int T = sc.nextInt();
        for (int i = 0; i < T; i++) {
            int n = sc.nextInt();
            int[] speed = new int[n];
            for (int j = 0; j < n; j++) {
                speed[j] = sc.nextInt();
            }
            //排序
            Arrays.sort(speed);
            f(n, speed);
        }
    }
    static void f(int n,int[] speed){
        int left =n;
        int ans =0;
        while (left>0){
            if(left ==1){
                ans +=speed[0];
                break;
            }
            if(left ==2){
                ans+= speed[1];
                break;
            }
            if(left ==3){
                ans+= speed[0]+speed[1]+speed[2];
                break;
            }
            else {
                //1,2出发,1返回,最后两名出发,从小到大排 思路就是前面走俩,后面走俩
                int s1 = speed[1] + speed[0] + speed[left - 1] + speed[1];
                //1,3出发,1返回,1,4出发,1返回,1,2过河 思路是用小的去带慢的,这儿举个例子就能感觉出来 带慢的带哪一个都可以,结果是一样的
                int s2 = speed[left - 1] + speed[left - 2] + 2 * speed[0];
                ans+= Math.min(s1,s2);
                left-=2;

            }
        }
        System.out.println(ans);
    }

}

三个贪心问题:区间问题:包括区间调度,区间选点,区间覆盖等问题  这就是很明显的最值问题了

3、区间调度问题:

有n项工作,每项工作分别在si时间开始,在ti时间结束.

对于每项工作,你都可以选择参与与否.如果选择了参与,那么自始至终都必须全程参与.

此外,参与工作的时间段不能重复(即使是开始的瞬间和结束的瞬间的重叠也是不允许的).

你的目标是参与尽可能多的工作,那么最多能参与多少项工作呢?

1≤n≤100000

1≤si≤ti≤10^9

输入:

第一行:n
第二行:n个整数空格隔开,代表n个工作的开始时间
第三行:n个整数空格隔开,代表n个工作的结束时间

样例输入:

5
12468
357910

样例输出:

3

说明:选取工作1,3,5

tip:

这个题要引入面向对象的思想,

comparable接口:

  1. 实现Comparable<T>接口后,还要覆盖public int compareTo(<T> object)方法

  2. * 如果两个对象相等返回0

  3. * 当前对象大于方法传过来的对象时,返回一个正整数

  4. * 当前对象小于方法传过来的对象时,返回一个负整数

考虑策略:考虑开始时间最早的,可能有一个从头到尾不可取,,,考虑时间短的,可能有一个在中间,选了他边上两个度不能选,不可取, 最后的策略是选结束最早的,选完一个后在起点大于结束点的情况中选结束最早的,因为要避免交叉问题

 

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[] s = new int[n];
        int[] t = new int[n];
        Job[] jobs = new Job[n];
        for(int i=0;i<n;i++){
            s[i] = sc.nextInt();
        }
        for (int i = 0; i < n; i++) {
            t[i] = sc.nextInt();
        }
        for (int i = 0; i < n; i++) {
            jobs[i] = new Job(s[i], t[i]);
        }
        Arrays.sort(jobs);
        int res = f(n, jobs);
        System.out.println(res);

    }
    static int f(int n,Job[] jobs){
        int cnt =1;
        int y =jobs[0].t;  //第一个是必选的,以为他是所有中结束最早的
        for(int i=0;i<n;i++){//所有中找,因为一下一个一定是结束最早的,所有只要他的开始时间大于上一个结束时间那就可以
            if(jobs[i].s>y){
                cnt++;
                y  =jobs[i].t;
            }
        }
        return cnt;
    }
    static class Job implements Comparable<Job>{
        int s;
        int t;

        public Job(int s, int t) {
            this.s = s;
            this.t = t;
        }

        @Override
        public int compareTo(Job other) {
            int x = this.t-other.t;
            if(x==0){
                return this.s - other.s; //因为按开始时间拍和安结束时间拍都应该从小到大,从下到大就不需要在换,若从大到小,还得返回-1

            }else {
                return x;
            }


        }
    }
}

4、区间选点:

这个问题的原始问题是:一段里有很多小区间,有的室友重复的,如何放一个点使他能够命中尽可能多的点。思路也是对结束时间进行排序,然后直接去选每个区间结束时的点,如果这个区间结束时的点命中了下一个区间,那就下一个区间就不用在选点了。那么如何判断这一个点是否命中了下一区间呢,就是选择下一区间开始时间大于上一结束时间的点,命中的就已经跳过了。

这个题的变体;难的地方在于不仅仅是需要命中,,而是需要达到这个区间的命中数。

Intervals
You are given n closed, integer intervals [ai, bi] and n integers c1, ..., cn.
Write a program that:
reads the number of intervals, their end points and integers c1, ..., cn from the standard input,
computes the minimal size of a set Z of integers which has at least ci common elements with interval [ai, bi], for each i=1,2,...,n,
writes the answer to the standard output.

Input
The first line of the input contains an integer n (1 <= n <= 50000) -- the number of intervals.
The following n lines describe the intervals. The (i+1)-th line of the input contains three integers ai,
bi and ci separated by single spaces and such that 0 <= ai <= bi <= 50000 and

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值