练习Day——12(贪心算法)

最大最小公倍数

问题描述
已知一个正整数N,问从1~N中任选出三个数,他们的最小公倍数最大可以为多少。

输入格式
输入一个正整数N。

输出格式
输出一个整数,表示你找到的最小公倍数。

样例输入
9

样例输出
504

数据规模与约定
1 <= N <= 106。

题目解析
1、第一想法
刚开始看到这个题目的时候第一想法就是枚举1~N中任意三个数,然后求它们的最小公倍数,从这些最小公倍数中再选择最大的一个即为正确答案,可是1<=N<=10^6, 如果要枚举任意三个数,最简单的做法就是三个嵌套的for循环,这样的话时间复杂度为O(N^3),会运行超时的。
2、正确解答
数学知识:如果三个数互为质数,那么这三个数的乘积便为它们的最小公倍数。

因为本题目中要求最小公倍数的最大值,那么可以直接从N向前看,找三个连续的互为质数的数,那么它们的乘积便是1~N最小公倍数的最大值。
有以下二种情况。
(1)、当N为奇数时,那么N,N-1,N-2互为质数,很明显NN-1N-2是1~N最小公倍数的最大值。
(2)、当N为偶数时,且能被3整除时,N-1,N-2,N-3互质,此时N-1N-2N-3是 1 ~ N最小公倍数的最大值;当N为偶数时,但不能被3整除时,N,N-1,N-3互质,此时NN-1N-3是1~N最小公倍数的最大值。

本题参考:https://blog.csdn.net/xiaofengcanyuelong/article/details/79271225

//最大的最小公倍数

import java.util.Scanner;

public class Main{
	public static void main(String[]args) {
		Scanner scan=new Scanner(System.in);
		long n=scan.nextInt();
		System.out.println(multiple(n));
	}

	private static long multiple(long n) {
		long max = 0;
		if(n%2!=0)
			max=n*(n-1)*(n-2);
		else if(n%2==0 && n%3==0)
			max=(n-1)*(n-2)*(n-3);
		else if(n%2==0 && n%3!=0)
			max=n*(n-1)*(n-3);
		return max;
	}
}

完美的代价

题目描述
回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
交换的定义是:交换两个相邻的字符
例如:mamad
第一次交换 ad : mamda
第二次交换 md : madma
第三次交换 ma : madam (回文!完美!)

输入
第一行是一个整数N,表示接下来的字符串的长度(N < = 8000) ;
第二行是一个字符串,长度为N.只包含小写字母。

输出
如果可能,输出最少的交换次数。
否则输出Impossible 。

样例输入
5
mamad

样例输出
3

题目解析
(1)正向从第一个字符开始,反向从最后一个字符开始依次往前,找到第一个和它相同的字符,如果找到了,就将找到的字符交换至最后一个位置;然后再进行新一轮的遍历,正向从第二个字符开始,反向从最后单数第二个字符开始依次往前执行和上述相同的操作;
(2)如果没有找到与当前第一个匹配的字符,那么当前字符有可能是在最中间位置。用一个布尔变量来判断当前字符是否可能为中间字符。如果将这个字符换到中间,那么以后的字符每次变换都会改变这个中间字符的位置。这个中间字符的左边是已经变换好的,只需要将中间字符的右边作变换就可以了,所以可以将中间字符在最后做变换(下面的代码没有在最后实际作变换,只是统计了它变换需要的次数),最后将右边的字符作回文处理就可以了,如果处理的过程中再次出现可能是中间的字符,那么这种情况就是不可能的了。

import java.util.Scanner;

//交换字母使其变成回文数

public class Main {
	private static int count=0;//记录交换次数
	private static boolean isMiddle=false;//记录当前比较的字符是否在最中间位置
	
	public static void main(String []args) {
		Scanner scan=new Scanner(System.in);
		int n=scan.nextInt();//字符串长度
		String str=scan.next();//读入字符串
		char[] ch=str.toCharArray();
		
		if(Huiwen(ch,0,n-1)) {
			System.out.println(count);
		}
		else {
			System.out.println("Impossible");
		}
	}
	
    //a是当前比较的起始位置,b当前比较的末位
	private static boolean Huiwen(char[] ch, int a, int b) {
		if(b<=a)
			return true;
		for(int i=b;i>a;i--) {//从最后一个字符开始依次向前
			if(ch[a]==ch[i]) {//如果找到字符和第一个字符相等
				swap(ch,i,b);//这个字符和最后一个字符交换位置
				count += b-i;//累加交换次数
				return Huiwen(ch,a+1,b-1);//递归
			}
		}
		//如果字符串中没有与当前第一个字符相等的字符,那么当前字符有可能是在最中间位置
		if(!isMiddle) {
			isMiddle=true;
			
			count += ch.length/2-a;
			return Huiwen(ch,a+1,b);
		}
		return false;
	}
    private static void swap(char[] ch, int a, int b) {
	    char tmp=ch[a];
	    //交换相邻字符
	    for(int i=a;i<b;i++)
		    ch[i]=ch[i+1];
	    
	    ch[b]=tmp;
    }
}	


拦截导弹(ALGO_13)

题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的 导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入
一行,为导弹依次飞来的高度

输出
两行,分别是最多能拦截的导弹数与要拦截所有导弹最少要配备的系统数

样例输入
389 207 155 300 299 170 158 65

样例输出
6
2

import java.util.Scanner;

public class Main {
	public static void main(String[]args) {
		Scanner scan=new Scanner(System.in);
		Scanner sc=new Scanner(scan.nextLine());//输入换行符结束
		
		int num[]=new int[1000];
		int n=0;
		while(sc.hasNext()) {
			num[n]=sc.nextInt();
			n++;
		}
		
		int dp[]=new int[n];
		
		//计算最多能拦截多少导弹,就是计算导弹高度序列的最长不上升序列长度
		int a=0,b=0;
		for(int i=0;i<n;i++) {
			dp[i]=1;
			for(int j=0;j<i;j++) {
				if(num[i]<num[j]) {
					dp[i]=Math.max(dp[i], dp[j]+1);
				}
			}
			if(dp[i]>a)
				a=dp[i];
		}
		
		//计算需要多少个导弹,就是计算最长不下降子序列长度
		for(int i=0;i<n;i++) {
			dp[i]=1;
			for(int j=0;j<i;j++) {
				if(num[i]>num[j]) {
					dp[i]=Math.max(dp[i], dp[j]+1);
				}
			}
			if(dp[i]>b)
				b=dp[i];
		}
		
		System.out.println(a);
		System.out.println(b);
	}
}

旅行家的预算(ALGO_15)

题目描述
一个旅行家想驾驶汽车以最少的费用从一个城市到另一个城市(假设出发时油箱是空的)。给定两个城市之间的距离D1、汽车油箱的容量C(以升为单位)、每升汽油能行驶的距离D2、出发点每升汽油价格P和沿途油站数N(N可以为零),油站i离出发点的距离Di、每升汽油价格Pi(i=1,2,……N)。计算结果四舍五入至小数点后两位。如果无法到达目的地,则输出“No Solution”。

输入
第一行为4个实数D1、C、D2、P与一个非负整数N;
接下来N行,每行两个实数Di、Pi。
  
输出
如果可以到达目的地,输出一个实数(四舍五入至小数点后两位),表示最小费用;否则输出“No Solution”(不含引号。
  
样例输入
275.6 11.9 27.4 2.8 2
102.0 2.9
220.0 2.2

样例输出
26.95

题目解析
将起点想象成第 0 个加油站,终点想象成 N+1 个加油站
第 0 个加油站距离起点的距离为 0,第 N+1 加油站距离起点的距离为 D1
第 0 个加油站的价格为 P,第 N+1 加油站的价格为 0
一、No Solution:如果两个加油站的距离大于加满油可以行驶的最大距离,那么无解
二、有解:从当前位置的下一个加油站寻找距离最近且便宜的加油站:
1、 如果能找到了
(1)如果能一次加油到达,那么加到刚好能到达便宜的加油站即可
(2)如果一次不能到达,那么先将油箱加满,到达行驶最大距离之前的那个加油站,再加到刚好行驶到的便宜那个加油站
2. 如果没找到,则加满油,行驶到最大距离之前的那个加油站,继续寻找
注意:终点的加油站价格为 0,所以最后一次加油加到刚好到达终点即可

参考:https://www.cnblogs.com/cao-lei/p/7243599.html

import java.util.Scanner;

public class Main {
	public static void main(String[]args) {
		Scanner scan=new Scanner(System.in);
		double d1,c,d2,p;
		int n;
		d1=scan.nextDouble();//两个城市之间的距离
		c=scan.nextDouble();//汽车油箱的容量
		d2=scan.nextDouble();//每升汽油能行驶的距离
		p=scan.nextDouble();//出发点每升汽油价格
		n=scan.nextInt();//沿途油站数(可为0)
		
		double dis[]=new double [n+2];//油站i离出发点的距离Di
		double pri[]=new double [n+2];//每升汽油价格Pi
		dis[0]=0; dis[n+1]=d1;
		pri[0]=p; pri[n+1]=0;
		
		double sum=0;//记录费用
		double rel=0;//到第i个加油站时的剩余油量
		
		for(int i=1;i<=n;i++) {
			dis[i]=scan.nextDouble();
			pri[i]=scan.nextDouble();
		}
		
		for(int i=0;i<=n;i++) {
			if(dis[i+1]-dis[i]>c*d2) {//如果两个加油站之间的距离大于加满油能行驶的最大距离,则无解
				System.out.println("No Solution");
				return ;
			}
		}
		scan.close();
		
		int i=0;
		while(i<=n) {
			int j;
			for(j=i+1;j<=n+1;j++) {
				if(dis[j]-dis[i]>c*d2) {
					j--;
					break;
				}
				if(pri[j]<=pri[i]) {
					break;
				}
			}
			if(pri[j]<=pri[i]) {
				sum+=pri[i]*((dis[j]-dis[i])/d2-rel);//加满油
				rel=0;//剩余油量归零
			}
			else {
				sum+=(c-rel)*pri[i];
				rel=c-(dis[j]-dis[i])/d2;
			}
			i=j;
		}
		
		System.out.printf("%.2f",sum);
	}	
}

纪念品分组(ALGO-34)

题目描述
元旦快到了,校学生会让乐乐负责新年晚会的纪念品发放工作。为使得参加晚会的同学所获得的纪念品价值 相对均衡,他要把购来的纪念品根据价格进行分组,但每组最多只能包括两件纪念品,并且每组纪念品的价格之和不能超过一个给定的整数。为了保证在尽量短的时 间内发完所有纪念品,乐乐希望分组的数目最少。
你的任务是写一个程序,找出所有分组方案中分组数最少的一种,输出最少的分组数目。

输入
输入包含n+2行:
第1行包括一个整数w,为每组纪念品价格之和的上限。
第2行为一个整数n,表示购来的纪念品的总件数。
第3~n+2行每行包含一个正整数pi (5 <= pi <= w),表示所对应纪念品的价格。

输出
输出仅一行,包含一个整数,即最少的分组数目。
  
样例输入
100
9
90
20
20
30
50
60
70
80
90

样例输出
6

数据规模和约定
50%的数据满足:1 <= n <= 15
100%的数据满足:1 <= n <= 30000, 80 <= w <= 200

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

public class Main {
	public static void main(String[]args) {
		Scanner scan=new Scanner(System.in);
		int w=scan.nextInt();//每组纪念品价格上限
		int n=scan.nextInt();//纪念品总数
		int p[]=new int[n];//各个纪念品的价格
		for(int i=0;i<n;i++)
			p[i]=scan.nextInt();
		scan.close();
		
		Arrays.sort(p);
		
		int sum=0;
		for(int i=0,j=n-1;i<=j;j--) {
			if(p[i]+p[j]<=w) {
				i++;
			}
			sum++;
		}
		System.out.println(sum);
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值