动态规划和贪心算法都属于递推算法,但是与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接口:
-
实现Comparable<T>接口后,还要覆盖public int compareTo(<T> object)方法
-
* 如果两个对象相等返回0
-
* 当前对象大于方法传过来的对象时,返回一个正整数
-
* 当前对象小于方法传过来的对象时,返回一个负整数
考虑策略:考虑开始时间最早的,可能有一个从头到尾不可取,,,考虑时间短的,可能有一个在中间,选了他边上两个度不能选,不可取, 最后的策略是选结束最早的,选完一个后在起点大于结束点的情况中选结束最早的,因为要避免交叉问题
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