贪心算法的应用

1. 部分背包问题

有n个物体,第i个物体的重量为wi,价值为vi。在总重量不超过C的情况下让总价值尽量高。

每一个物体都可以只取走一部分,价值和重量按比例计算。

求最大总价值

注意:每个物体可以只拿一部分,因此一定可以让总重量恰好为C。

package 贪心算法;

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

/**
 * @author: DreamCode
 * @file: 部分背包问题.java
 * @time: 2022-4-1-14:41:18
 * @method: 贪心选择:总是选择单价较高的物品
 */
public class 部分背包问题 {

	static class obj implements Comparable<obj> {
		double w;
		double v;

		public obj(double w, double v) {
			super();
			this.w = w;
			this.v = v;
		}

		public double get_price() {
			return this.getV() / this.getW();
		}

		@Override
		public int compareTo(obj o) {
			// compareTo的中心思想就是比较两个值的大小,如果小了就返回-1,大了就返回1,相等就返回0
			double x = this.get_price() - o.get_price();
			if (x == 0)
				return 0;
			else if (x < 0) { // 小了要变大
				return 1;
			} else { // 大了要变小
				return -1;
			}
		}

		public double getW() {
			return w;
		}

		public void setW(double w) {
			this.w = w;
		}

		public double getV() {
			return v;
		}

		public void setV(double v) {
			this.v = v;
		}

	}

	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		int n = scanner.nextInt();
		int c = scanner.nextInt();
		obj[] objs = new obj[n];
		for (int i = 0; i < n; i++) {
			objs[i] = new obj(scanner.nextDouble(), scanner.nextDouble());
		}
		Arrays.sort(objs);
		double ans = greedy(objs, c);
		System.out.println(ans);

	}

	private static double greedy(obj[] objs, int c) {
		// TODO 贪心选择:总是选择单价较高的物品
		int len = objs.length;
		double res = 0;
		for (int i = 0; i < len; i++) {
			if (c >= objs[i].getW()) {
				res += objs[i].getV();
				c -= objs[i].getW();
			} else {
				res += c * objs[i].get_price();
				break;
			}
		}
		return res;
	}

}

2. 乘船问题

有n个人,第i个人重量为wi。每艘船的最大载重量均为C,且最多只能乘两个人。用最少的船装载所有人。 求需要船的数量

package 贪心算法;

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

/**
 * @author: DreamCode
 * @file: 乘船问题.java
 * @time: 2022-4-1-15:10:05
 * @method: 贪心策略:考虑最轻的人i,如果每个人都无法和他一起坐船(重量和超过C),则唯一的方案是每个人坐一艘
 *          否则,他应该选择能和他一起坐船的人中最重的一个j
 */
public class 乘船问题 {

	public static void main(String[] args) {
		int[] w = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
		int n = w.length;
		int c = 10;

		Arrays.sort(w);
		int cntOfPerson = n;
		int cntOfBoat = 0;
		int p1 = 0;
		int p2 = n - 1;
		while (cntOfPerson > 0) {
			if (p1 + p2 > c) {
				p2--;
				cntOfPerson--;
				cntOfBoat++;
			} else {
				p1++;
				p2--;
				cntOfPerson -= 2;
				cntOfBoat++;
			}
		}
		System.out.println(cntOfBoat);
	}
}

3. 区间调度问题

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

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

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

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

1≤n≤100000

1≤si≤ti≤10^9

输入:

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

样例输入:

5
1 3 1 6 8
3 5 2 9 10

样例输出:

3

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

package 贪心算法;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

/**
 * @author: DreamCode
 * @file: 区间调度问题.java
 * @time: 2022年3月30日-下午8:24:53
 * @思路:  贪心选择区间问题,总是优先选择最快结束的任务
 */
public class 区间调度问题 {

	static class Job implements Comparable<Job> {
		private int s;
		private int e;
		
		public Job(int s, int e) {
			super();
			this.s = s;
			this.e = e;
		}

		@Override
		public int compareTo(Job o) {
			int x =  this.getE()-o.getE();
			if(x==0) return this.getS()-o.getS();
			return x;
		}

		public int getS() {
			return s;
		}

		public void setS(int s) {
			this.s = s;
		}

		public int getE() {
			return e;
		}

		public void setE(int e) {
			this.e = e;
		}
		
	}
	public static void main(String[] args) {
		Scanner scanner=new Scanner(System.in);
		int n=scanner.nextInt();
		int[] s=new int[n];
		int[] e=new int[n];
		Job[] jobs=new Job[n];
		for(int i=0;i<n;i++) {
			s[i]=scanner.nextInt();
		}
		for(int i=0;i<n;i++) {
			e[i]=scanner.nextInt();
		}
		for(int i=0;i<n;i++) {
			jobs[i]=new Job(s[i], e[i]);
		}
		Arrays.sort(jobs);
		List<Job> ans = greddy(jobs);
		System.out.println(ans.size());

	}
	private static List<Job> greddy(Job[] jobs) {
		// TODO 贪心选择
		List<Job> list=new ArrayList<>();
		list.add(jobs[0]);//第一个任务一定会选择
		int end=jobs[0].getE(); //记录当前调度的结束时间
		for(int i=1;i<jobs.length;i++) {
			if(jobs[i].getS()>end) {  //当前任务能选择
				list.add(jobs[i]);
				end=jobs[i].getE();
			}
		}
		return list;
	}

}

4. 区间覆盖问题

Farmer John is assigning some of his N (1 <= N <= 25,000) cows to do some cleaning chores around the barn.

He always wants to have one cow working on cleaning things up and has divided the day into T shifts (1 <= T <= 1,000,000),

the first being shift 1 and the last being shift T.

Each cow is only available at some interval of times during the day for work on cleaning.

Any cow that is selected for cleaning duty will work for the entirety of her interval.

Your job is to help Farmer John assign some cows to shifts so that (i) every shift has at least one cow assigned to it,

and (ii) as few cows as possible are involved in cleaning. If it is not possible to assign a cow to each shift, print -1.

Input
Line 1: Two space-separated integers: N and T

Lines 2…N+1: Each line contains the start and end times of the interval during which a cow can work.

A cow starts work at the start time and finishes after the end time.
Output
Line 1: The minimum number of cows Farmer John needs to hire or -1 if it is not possible to assign a cow to each shift.
Sample Input
3 10
1 7
3 6
6 10
Sample Output
2

package 贪心算法;

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

/**
 * @author: DreamCode
 * @file: 区间覆盖问题.java
 * @time: 2022年3月30日-下午9:05:15
 * @思路: 把各区间按照a从小到大排序。如果区间1的起点不是s,无解。否则起点在s的最长区间。选择[ai,bi]后,新的起点设置成bi。直至覆盖整个线段。
 */
public class 区间覆盖问题 {

	static class line implements Comparable<line> {
		int s;
		int e;

		public line(int s, int e) {
			super();
			this.s = s;
			this.e = e;
		}

		public int getS() {
			return s;
		}

		public void setS(int s) {
			this.s = s;
		}

		public int getE() {
			return e;
		}

		public void setE(int e) {
			this.e = e;
		}

		@Override
		public int compareTo(line o) {
			int x = this.getS() - o.getS();
			if (x == 0)
				return this.getE() - o.getE();
			return x;
		}
	}

	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		int N = scanner.nextInt();
		int T = scanner.nextInt();
		line[] lines = new line[N];
		for (int i = 0; i < N; i++) {
			lines[i] = new line(scanner.nextInt(), scanner.nextInt());
		}
		Arrays.sort(lines);
		int start = 1;
		int end = 1;
		int ans = 1;
		for (int i = 0; i < N; i++) {
			int s = lines[i].getS();
			int e = lines[i].getE();
			if (i == 0 && s > 1) {// 第一条线段不满足S起点,则不可能找到覆盖所指定区间的线段
				break;
			}
			if (s <= start) { // 当前区间有可能覆盖start
				end = Math.max(e, end); // 更新更右的端点
			} else { // 开始下一个区间
				ans++; // 上一个目标覆盖已经达成,计数加1
				start = end + 1; // 更新起点,设置一个新的覆盖目标
				if (s <= start) {
					end = Math.max(e, end);
				} else {
					break;
				}
			}
		}
		if (end < T) {
			System.out.println(-1);
		} else {
			System.out.println(ans);
		}

	}

}

5. 区间选点问题

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 1 <= ci <= bi - ai+1.

Output
The output contains exactly one integer equal to the minimal size of set Z
sharing at least ci elements with interval [ai, bi], for each i=1,2,…,n.
Sample Input
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
Sample Output
6

package 贪心算法;

import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;

/**
 * @author: DreamCode
 * @file: 区间选点问题.java
 * @time: 2022年3月30日-下午8:40:15
 * @思路: 按照区间的右端点进行排序。对每个线段判断时,先从左到右判断线段已经包含多少个点,都从右端点开始进行打缺少的点。
 */
public class 区间选点问题 {

	static class line implements Comparable<line>{
		private int s,e,num;

		public line(int s, int e, int num) {
			super();
			this.s = s;
			this.e = e;
			this.num = num;
		}

		public int getS() {
			return s;
		}

		public void setS(int s) {
			this.s = s;
		}

		public int getE() {
			return e;
		}

		public void setE(int e) {
			this.e = e;
		}

		public int getNum() {
			return num;
		}

		public void setNum(int num) {
			this.num = num;
		}

		@Override
		public int compareTo(line o2) {
			int x=this.getE()-o2.getE();
			if(x==0) return this.getS()-o2.getS();
			return x;
		}
	}
	public static void main(String[] args) {
		Scanner scanner=new Scanner(System.in);
		int n=scanner.nextInt();
		line[] lines=new line[n];
		for(int i=0;i<n;i++) {
			lines[i]=new line(scanner.nextInt(), scanner.nextInt(), scanner.nextInt());
		}
		Arrays.sort(lines);
		int ans=greed(lines);
		System.out.println(ans);
	}
	private static int greed(line[] lines) {
		// TODO get the least points by greed
		int res=0;
		boolean[] rec=new boolean[lines[lines.length-1].getE()+1];
		Arrays.fill(rec, false);
		for(int i=0;i<lines.length;i++) {
			int s=lines[i].getS();
			int e=lines[i].getE();
			int num=lines[i].getNum();
			for(int j=s;j<=e;j++) {//查询当前已经有的点数目
				if(rec[j]) {
					num--;
				}
			}
			if(num<=0) {//当前线段最少点数已经满足
				continue;
			}else {  //从右端点逐个打点
				for(int j=e;j>=s;j--) {
					if(!rec[j]) {//当前点未打点
						rec[j]=true;
						num--;
						if(num==0) {//满足线段打的最少点
							break;
						}
					}
				}	
			}
		}
		for(int i=0;i<rec.length;i++) {
			if(rec[i]) {
				res++;
			}
		}
		return res;
	}

}

6. 小船过河_往返问题

A group of N people wishes to go across a river with only one boat, which can at most carry two persons.
Therefore some sort of shuttle arrangement must be arranged in order to row the boat back and forth so that all people may cross.
Each person has a different rowing speed; the speed of a couple is determined by the speed of the slower one.
Your job is to determine a strategy that minimizes the time for these people to get across.

Input

The first line of the input contains a single integer T (1 <= T <= 20), the number of test cases.
Then T cases follow.
The first line of each case contains N, and the second line contains N integers giving the time for each people to cross the river.
Each case is preceded by a blank line. There won’t be more than 1000 people and nobody takes more than 100 seconds to cross.

Output

For each test case, print a line containing the total number of seconds required for all the N people to cross the river.

Sample Input
1
4
1 2 5 10
Sample Output
17

package 贪心算法;

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

/**
 * @author: DreamCode
 * @file: 小船过河_往返.java
 * @time: 2022年3月30日-下午7:56:30
 * @思路: 将所有人的花费时间按照从小到达排序。每轮将最慢的两人带过河,有两种方案:第一种是都靠第1人来回带,
 *      第二种是第1人和第2人过去,然后第一人将船送回,然后第n人与第n-1人过河,然后第2人再将船带回;两种方案始终满足将最慢的两人带走,每轮选择最优的方案,即构成局部最优解
 *      人数为1,2,3人需要特殊考虑
 */
public class 小船过河_往返 {

	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		int T = scanner.nextInt();
		while (T > 0) {
			int n = scanner.nextInt();
			int[] arr = new int[n];
			for (int i = 0; i < n; i++) {
				arr[i] = scanner.nextInt();
			}
			Arrays.sort(arr);
			int ans = greedy(arr);
			System.out.println(ans);
			T--;
		}

	}

	private static int greedy(int[] arr) {
		// TODO 贪心选择
		int n = arr.length - 1; // 最后一人
		int ans = 0;
		while (n >= 0) { // 每次选择最后两个人,所以最终
			if (n == 0) {// 当前只有一人
				ans += arr[0];
				break;
			} else if (n == 1) {
				ans += arr[1];
				break;
			} else if (n == 2) {
				ans += arr[0] + arr[1] + arr[2];
				break;
			} else { // 当前多于两人
				int a = arr[n] + arr[0] + arr[n - 1] + arr[0]; // 方案1
				int b = arr[1] + arr[0] + arr[n] + arr[1];// 方案2
				ans += Math.min(a, b);
				n -= 2;
			}
		}
		return ans;
	}

}

7. 硬币支付问题

有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

package 贪心算法;

import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;

/**
 * @author: DreamCode
 * @file: 硬币支付问题.java
 * @time: 2022年3月30日-下午4:41:49
 * @思路: 总是优先且多的选择较大的硬币面值。选择过程采用深搜
 */
public class 硬币支付问题 {

	static int[] value= {1, 5, 10, 50, 100, 500};
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		Integer[] coins = new Integer[6];
		for (int i = 0; i < coins.length; i++) {
			coins[i] = scanner.nextInt();
		}
		int A = scanner.nextInt();
		int ans = DFS(A, coins, 5);
		System.out.println(ans);
	}

	private static int DFS(int A, Integer[] coins, int k) {
		if (k == 0) {
			return A;
		}
		if (A >= value[k]) {
			int count = Math.min(coins[k], A / value[k]);
			return count + DFS(A - count * value[k], coins, k - 1);
		} else {
			return DFS(A, coins, k - 1);
		}

	}

}

8. 字典序最小问题

字典序最小问题

给一个定长为N的字符串S,构造一个字符串T,长度也为N。

起初,T是一个空串,随后反复进行下列任意操作

  1. 从S的头部删除一个字符,加到T的尾部
  2. 从S的尾部删除一个字符,加到T的尾部

目标是最后生成的字符串T的字典序尽可能小

1≤N≤2000
字符串S只包含大写英文字母

输入:字符串S
输出:字符串T

package 贪心算法;

import java.util.Scanner;

/**
 * @author: DreamCode
 * @file: 字典序最小问题.java
 * @time: 2022-4-1-14:12:52
 * @method: 将S字符串反转得到S1,分别比较S与S1的字典序,贪心选择字典序较大的字符串的首位字符,更新选择的字符串为该串从1到len的字串;指导选择的串的长度达到N
 */
public class 字典序最小问题 {

	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		String string = scanner.next();
		String ans = greedy(string);
		System.out.println(ans);

	}

	private static String greedy(String string) {
		// TODO 贪心选择字符,贪的不是当前,而是大体
		int len = string.length();
		String s1 = new StringBuilder(string).reverse().toString();
		StringBuilder res = new StringBuilder();
		while (res.length() < len) {
			if (string.compareTo(s1) < 0) { // 贪心选择subStr1的字符
				res.append(string.charAt(0));
				string = string.substring(1);
			} else {
				res.append(s1.charAt(0));
				s1 = s1.substring(1);
			}
		}
		return res.toString();
	}

}

9. 最优装载问题

给出n个物体,第i个物体重量为wi。选择尽量多的物体,使得总重量不超过C。

package 贪心算法;

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

/**
 * @author: DreamCode
 * @file: 最优装载问题.java
 * @time: 2022-4-1-14:35:38
 * @method: 贪心选择:尽可能选择轻的物体
 */
public class 最优装载问题 {

	public static void main(String[] args) {
		Scanner scanner=new Scanner(System.in);
		int n=scanner.nextInt();
		int C=scanner.nextInt();
		int[] w=new int[n];
		for(int i=0;i<n;i++) {
			w[i]=scanner.nextInt();
		}
		Arrays.sort(w);
		int ans=greedy(w,C);
		System.out.println(ans);

	}

	private static int greedy(int[] w, int c) {
		// TODO 贪心选择:尽可能选择轻的物品
		int res=0;
		for(int i=0;i<w.length;i++) {
			if(w[i]<=c) {
				res++;
				c-=w[i];
			}else {
				break;
			}
		}
		return res;
	}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦码城

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值