贪心法(选择最少的硬币个数)(区间问题:选择结束最早的TreeMap)(字典序最小问题)(切割法二叉树,哈夫曼树))

3 篇文章 0 订阅

硬币问题在这里插入图片描述

首先10的9次方这个要注意,不可能用暴力解法

优先使用面值大的硬币

搜索算法和动态规划算法是在多种策略中选取最优解
而贪心算法则不同,它是遵循某种规则,不断地选取当前最优策略。例如在本题中“优先使用面值最大的硬币”就是在计算过程中所遵循的规则。并且,我们只考虑“尽可能多的使用面值大的硬币”这一种当前最优策略

这里的硬币问题也相当与一种背包问题,那么比起用下节要介绍的动态规划算法求解,贪心算法更简单更高效

package 程序设计竞赛;

import java.util.Scanner;

public class 贪心¥硬币问题 {
	//硬币的面值
	final static long[] V=new long[]{1,5,10,50,100,500};
	//long是基本类型,Long是对象类型
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		long[] number=new long[6];
		for(int i=0;i<6;i++){
			number[i]=sc.nextLong();
		}
		long A=sc.nextLong();
		//贪心问题
		int ans=0;
		for(int i=5;i>=0;i--){
			//使用硬币的枚数
			long t=Math.min((A/V[i]), number[i]);
			A-=t*V[i];
			ans+=t;//用了几个硬币
		}
		System.out.println(ans);
	}

}

区间问题

在这里插入图片描述
在这里插入图片描述

题目这个是要进行画图看的才清楚。
在可选的工作(也就是和目前已选的工作都不重叠的工作中)每次都选取开始时间最早的工作
在这里插入图片描述
因此,如果我们不慎重地选择了一个正确的规则,就会得到错误的算法。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

HashMap和TreeMap

在这里插入图片描述

package 程序设计竞赛;

import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;

public class 贪心¥区间问题 {
	//final static int Max_n=100000;
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		long [] s=new long[n];
		long [] t=new long[n];
		for(int i=0;i<n;i++){
			s[i]=sc.nextLong();
		}
		for(int i=0;i<n;i++){
			t[i]=sc.nextLong();
		}
		//用于对工作排序的键值对,(书上写的是pair)
		Map<Long,Long> map=new TreeMap<>();
		//贪心
		//为了然后接受时间早的工作排在前面,把T存入first,把S存入second
		for(int i=0;i<n;i++){
			map.put(t[i], s[i]);
		}
		
		//t是最后所选工作的结束时间
		int ans=0;long m=0;
		for(int i=0;i<n;i++){
			if(m<map.get(t[i])){
				ans++;
				m=t[i];
			}
		}
		System.out.println(ans);
	}

}

在这里插入图片描述

字典序最小问题

在这里插入图片描述
在这里插入图片描述

字典序比较类的问题经常能用到上贪心法

在这里插入图片描述
在这里插入图片描述

package 程序设计竞赛;

import java.util.Scanner;

public class 贪心¥字典序最小问题 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		String s=sc.next();
		//s[a],s[a+1]``
		char[] ss=s.toCharArray();
		int a=0;
		int b=n-1;
		while(a<=b){
			//将从左起和右起的字符串比较
			boolean left=false;
			//for(int i=0;a+i<=b;i++){
				//当左边的字符串小于右边的时候
				if(ss[a]<ss[b]){
					left=true;
					//break;
				}else if(ss[a]>=ss[b]){
					left=false;
					//break;
				}
			//}
			if(left)
				System.out.print(ss[a++]);
			else{
				System.out.print(ss[b--]);
			}
		}
		
	}

}

Saruman’s Army

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package 程序设计竞赛;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class 贪心¥SarumanArmy {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		int r=sc.nextInt();
		int[] x=new int[n];
		//Map<Integer,Integer> map=new HashMap<>();
		for(int i=0;i<n;i++){
			x[i]=sc.nextInt();
		}
		//要先排序
		Arrays.sort(x);
		int i=0,ans=0;
		while(i<n){
			//s是没有被覆盖的最左边的位置
			//int s=x[i];
			//i++;
			//上面两句可以合为
			//System.out.println("i0="+i);
			int s=x[i++];
			//System.out.println("si="+i);
			//System.out.println("s="+s);
			//一直向右前进直到距s的距离大于r的点
			while(i<n&&x[i]<=s+r)
				i++;
			//System.out.println("i="+i);
			//p位新加上标记的位置
			int p=x[i-1];//这个点是要标记的
			//一直享有前进知道距p的距离大于r的点
			while(i<n&&x[i]<=p+r)
				i++;
			
			//System.out.println("i1="+i);
			ans++;
			//两个一循环
		}
		System.out.println(ans);
	}

}

Fence Repair

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对于最优解来讲,最短的板应当是深度最大的叶子节点之一。所以与这个叶子节点同一深度的兄弟节点一定存在,并且由于同样是最深的叶子节点,所以应该对应于次短的板
在这里插入图片描述
输入点生成树

package 程序设计竞赛;

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

public class FenceRepair {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		int[] len=new int[n];
		for(int i=0;i<n;i++){
			len[i]=sc.nextInt();
		}
		long ans=0;
		//将两块板平和
		//这个不能直接排序,因为这个如果有重复元素构成二叉树不可以
		while(n>1){
			int mill1=0,mill2=1;
			if(len[mill1]>len[mill2])
				swap(mill1,mill2);
			for(int i=2;i<n;i++){
				if(len[i]<len[mill1]){
					mill2=mill1;
					mill1=i;
				}else if(len[i]<len[mill2]){
					mill2=i;
				}
			}
			//将两块板拼合
			int t=len[mill1]+len[mill2];
			ans+=t;
			len[mill1]=t;
			len[mill2]=len[n-1];
			n--;
		}
	}

	private static void swap(int mill1, int mill2) {
		// TODO Auto-generated method stub
		int t=mill1;
		mill1=mill2;
		mill2=t;
	}

}

霍夫曼编码

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

向上Claire

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

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

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

打赏作者

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

抵扣说明:

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

余额充值