算式900
题目链接: 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;
}
}
谈判
题目链接: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);
}
}
幸运数
题目链接: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了,唯一要注意的就是题目上说的是:输出位于 m 和 n 之间的幸运数的个数(不包含 m 和 n ),这个可千万别漏掉了
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
题目链接:https://www.lanqiao.cn/problems/1591/learning/
这个题本来想把数组写出来的,但是看看下面的测试用例感觉不太现实,只能是找找数学方法了
这个题想到了一种方法,题目给的小用例通过了,自己编的几个小用例也通过了,但是提交之后答案是错的,有些懵,还不知道哪错了。。
我这这里画个图,大家先对照代码看看吧
变量解释:
- list:第i段及之前的所有元素的个数
- left,right:输入的元素位置
- leftSegNum,rightSegNum:元素的段号,每一次循环是一个段
- leftIdx,rightIdx:元素是一个段中的第几个元素
- leftCount:从leftIdx到该段结束的所有元素之和
- rightCount:从该段开始到rightIdx的所有元素之和
程序输出:
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;
}
}