蓝桥杯31天冲刺之十五 [java]

算式900

image-20220322130823286

题目链接: https://www.lanqiao.cn/problems/649/learning/

这个题和昨天的带分数(全排列)非常类似,可以说是它的简化版

在这个题中,也是需要先得到10个数字的全排列,然后按照一定的规则去分割,与昨天不同的是,今天的题目把分割的规则已经给好的,因此对于每一种排列的情况就不用再自己去在check函数中遍历当前这种排列的所有分割情况,只需要判断分割成4 4 2这种格式能不能得到需要的结果就行了,至于对于判断0开头的,可以直接封装在getVal函数中,如果是0开头的,则直接返回-1,然后在check函数中判断结果是不是-1就行了,代码整体看起来比较简洁

排列树和子集树就是现成的模板了,如果是在理解不了记下就好了,在这里把模板给出来,如果想理解的话可以去看看这篇博客,是我们上课讲到这里时候一个同学做的笔记

子集树

void Backtrack (int t){
    if (t>n)
        Output(x);
    else
        for (int i=0; i<=1; i++){
            x[t]=i;
            if (Constraint(t)&&Bound(t))
                Backtrack(t+1);
        }
}

排列树

void Backtrack (int t){
    if (t>n)
        Output(x);
    else
        for (int i=t; i<=n; i++){
            Swap(x[t], x[i]);
            if (Constraint(t)&&Bound(t))
                Backtrack(t+1);
            Swap(x[t], x[i]);
        }
}
package daily;

public class day3_22_1 {
	static int[] arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

	public static void main(String[] args) {
		backtrack(0);
	}

	/**
	 * 回溯得到所有的排列情况
	 * 
	 * @param index
	 */
	private static void backtrack(int index) {
		if (index == arr.length) {
			check();
		} else {
			for (int j = index; j < arr.length; j++) {
				swap(j, index);
				backtrack(index + 1);
				swap(j, index);
			}
		}
	}

	/**
	 * 交换数组中的两个元素
	 * 
	 * @param j
	 * @param i
	 */
	private static void swap(int j, int i) {
		int temp = arr[j];
		arr[j] = arr[i];
		arr[i] = temp;
	}

	/**
	 * 检查当前这种组合是否可以得到答案
	 */
	private static void check() {
		int a = getVal(0, 4);
		int b = getVal(4, 8);
		int c = getVal(8, 10);
		if (a == -1 || b == -1 || c == -1) {
			// 三个数字中有一个是以0开头的
			return;
		}
		if ((a - b) * c == 900) {
			// 找到结果并且与题目中的结果不一致时输出
			if (a != 5012 && b != 4987 && c != 36) {
				System.out.printf("(%d-%d)*%d=900", a, b, c);
			}
		}
	}

	/**
	 * 将范围 [i,j) 中的值转化为int返回,如果是以0开头的数字则返回-1
	 * 
	 * @param i
	 * @param j
	 * @return
	 */
	private static int getVal(int i, int j) {
		if (arr[i] == 0) {
			return -1;
		}
		int count = 0;
		for (int k = i; k < j; k++) {
			count = count * 10 + arr[k];
		}
		return count;
	}
}

谈判

image-20220322131533412

题目链接:https://www.lanqiao.cn/problems/545/learning/

这个题题目读完心里想的就是贪!,hhhhh,准确的说是贪心,要想要整体的花费最小,那肯定是每次都选择人数最少的两个部落联合,这样可以保证每次花费最少,那么最后得到的总花费肯定是最小的

然后这里使用的数据结构是一个优先级队列,我自己理解的是这个其实就是一个最小堆,每次调用poll会把最小值返回回来,时间复杂度是O(1),添加一个新元素的时间复杂度是O(logn),还是很好用的,具体的细节我也没有多了解,不太熟的可以自己上网搜一下这个数据结构。

不然就需要自己去维护一个数组,保证每次取到的都是最小值,如果不这样,每次都是遍历数组找最小值的话时间复杂度就是O(n)了,数据多一点就可能会超时了

package daily;

import java.util.PriorityQueue;
import java.util.Scanner;

/**
 * https://www.lanqiao.cn/problems/545/learning/
 * 
 * @author Jia
 *
 */
public class day3_22_2 {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);

		int n = sc.nextInt();
		PriorityQueue<Integer> queue = new PriorityQueue<>();
		for (int i = 0; i < n; i++) {
			queue.add(sc.nextInt());
		}
		int ans = 0;
		while (queue.size() != 1) {
			int val_1 = queue.poll();
			int val_2 = queue.poll();
			ans += val_1 + val_2;
			queue.add(val_1 + val_2);
		}
		System.out.println(ans);
	}
}

幸运数

image-20220322132937877

题目链接:https://www.lanqiao.cn/problems/214/learning/

这个题理解起来不是很难,但是能不能理解正确就有点难说了,首先是删除的条件应该是什么?这个就比较难把握,删除的条件是目标位置的序号除以当前这个位置的数余数为0时,才把目标位置的元素删掉,这个需要自己好好体会一下,如果这个理解不了并且下面的解释也看不太明白建议把代码copy到ide里面一步一步调试看一下

我使用的是数组存储每个元素,如果arr[i]为0表示这个元素被删掉了,如果不为0,则arr[i]是数字i当前的序号,对于数组的定义必须搞明白才能看懂下面的代码

然后使用双层循环,外层循环保证把每个元素都对数组进行了一遍过滤,把满足条件的都删掉了,内层循环是具体的过滤过程,这里使用count来记录在这次循环中删掉了多少元素,用来维护数字i的序号arr[i],30行if的位置应该比较难理解,这个对应的就是删除的条件,数字j的序号可以整除数字i的时候,就要把数字j从我们的数组中删掉(arr[j]=0),同时count++表示删掉了一个元素,然后是else里面维护数字j的序号,前面删掉了count个元素,那么后面的数字序号理所应当的就要往前移动count位,所以直接减掉就好了

最后直接统计结果输出就ok了,唯一要注意的就是题目上说的是:输出位于 mn 之间的幸运数的个数(不包含 mn ),这个可千万别漏掉了

package daily;

import java.util.Scanner;

/**
 * https://www.lanqiao.cn/problems/214/learning/
 * 
 * @author Jia
 *
 */
public class day3_22_3 {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int m = sc.nextInt();
		int n = sc.nextInt();
		sc.close();
		int[] arr = new int[n + 1];
		// 初始化数组每一位对对应的序号
		for (int i = 0; i < arr.length; i++) {
			arr[i] = i;
		}

		// 从头到尾遍历数组,把满足条件的项的序号变为0,然后把不满足条件的项的序号减去count
		for (int i = 2; i < arr.length; i++) {
			if (arr[i] == 0) {
				continue;
			}
			int count = 0;// 标记前面有几个元素被删掉了
			for (int j = i; j < arr.length; j++) {
				if (arr[j] != 0 && arr[j] % i == 0) {
					// 如果目标元素不为0,并且目标元素的序号除以当前位置的下标为0,则说明满足条件,直接删掉
					count++;
					arr[j] = 0;
				} else if (arr[j] != 0) {
					// 不满足条件则修改序号
					arr[j] -= count;
				}
			}
		}

		// 统计结果
		int ans = 0;
		for (int i = m + 1; i < n; i++) {
			if (arr[i] != 0) {
				ans++;
			}
		}
		System.out.println(ans);
	}
}

123

image-20220322140217040

题目链接:https://www.lanqiao.cn/problems/1591/learning/

这个题本来想把数组写出来的,但是看看下面的测试用例感觉不太现实,只能是找找数学方法了

这个题想到了一种方法,题目给的小用例通过了,自己编的几个小用例也通过了,但是提交之后答案是错的,有些懵,还不知道哪错了。。

我这这里画个图,大家先对照代码看看吧

变量解释:

  • list:第i段及之前的所有元素的个数
  • left,right:输入的元素位置
  • leftSegNum,rightSegNum:元素的段号,每一次循环是一个段
  • leftIdx,rightIdx:元素是一个段中的第几个元素
  • leftCount:从leftIdx到该段结束的所有元素之和
  • rightCount:从该段开始到rightIdx的所有元素之和

image-20220322152609863

程序输出:

image-20220322153022983

package daily;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.ArrayList;

/**
 * https://www.lanqiao.cn/problems/1591/learning/
 * 
 * @author Jia
 *
 */
public class day3_22_4 {
	static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	static StreamTokenizer in = new StreamTokenizer(br);

	static int nextInt() throws IOException {
		in.nextToken();
		return (int) in.nval;
	}

	static ArrayList<Integer> list = new ArrayList<>();// 存储到第i段结束总共有多少个数字

	public static void main(String[] args) throws IOException {
		int T = nextInt();
		init();
//		getSegNum(1);

		for (int j = 0; j < T; j++) {
			int left = nextInt();
			int right = nextInt();
			int ans = getAns(left, right);
			System.out.println(ans);
		}

	}

	/**
	 * 初始化list数组
	 */
	private static void init() {
		int i = 1;
		long val = 0;
		long sum = 0;
		double end = 10e8;// 这里有问题,如果比10e8小结果就没问题,如果比10e8大结果会出错,按照题目这里应该设置10e12
		list.add(0);
		while (true) {
			val = val + i;
			if (val > end) {
				break;
			}
			list.add((int) val);
			i++;
		}
	}

	/**
	 * 根据输入求得答案
	 * 
	 * @param left
	 * @param right
	 * @return
	 */
	private static int getAns(int left, int right) {
		int ans = 0;
		int leftSegNum = getSegNum(left);
		int rightSegNum = getSegNum(right);
		if (leftSegNum == rightSegNum) {
			// 两个在一个片段内
			ans = sum(left, right);
		} else {
			// 两个不在一个片段内
			int leftIdx = left - list.get(leftSegNum - 1);// 第leftSegNum段中的第leftIdx个数
			int rightIdx = right - list.get(rightSegNum - 1);

			int leftCount = sum(leftIdx, leftSegNum); // 从leftIdx到这个段最后一个数的和
			int rightCount = sum(1, rightIdx);

			// 两个数字不是挨着的,中间有其他段
			for (int i = leftSegNum + 1; i < rightSegNum; i++) {
				ans += list.get(i);
			}
			ans += leftCount + rightCount;
		}

		return ans;
	}

	/**
	 * 求 [left,right] 的所有数字之和
	 * 
	 * @param left
	 * @param right
	 * @return
	 */
	private static int sum(int left, int right) {
		return (left + right) * (right - left + 1) / 2;
	}

	/**
	 * 求得第num个数字的段号 num的范围是[1,+00)
	 * 
	 * @param num
	 * @return
	 */
	private static int getSegNum(int num) {
		int left = 0;
		int right = list.size() - 1;
		while (left <= right) {
			int mid = (right - left) / 2 + left;
			int midNum = list.get(mid);
			if (midNum == num) {
				return mid;
			} else if (midNum > num) {
				right = mid - 1;
			} else {
				left = mid + 1;
			}
		}
		return left;
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Hydrion-Qlz

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

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

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

打赏作者

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

抵扣说明:

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

余额充值