一、区间k大数查询(简单)(模拟)
直接模拟就可以,个人觉得题目不严谨,没有说重复元素,也没有说重复元素算并列还是算新的最大数。
取出L - R的数,排序,找第k大。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(); // 序列长度
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i]= sc.nextInt(); // 序列中的数
}
int m = sc.nextInt(); //表示询问的个数
int[] res = new int[m]; // 存储询问的结果
for (int i= 0; i < m; i++) {
int l = sc.nextInt();
int r = sc.nextInt();
int k = sc.nextInt();
int[] helper = new int[r - l + 1]; // 存 l - j 区间的数
for (int j = 0; j < r - l + 1; j++) {
helper[j] = arr[l + j - 1]; // 将 l , j 区间的数取出存入新数组
}
Arrays.sort(helper); // 对数组进行排序
res[i] = helper[helper.length - k]; // 得到l , k 区间中第 k 大的数
}
// 遍历res数组,输出每次询问序列的结果
for(int i = 0 ; i < res.length; i++) {
System.out.println(res[i]);
}
}
}
二、最大最小公倍数(中等)(数论)
害怕这种数论题…,一开始想法是求连续三个数的gcd,如果gcd=1,就直接返回三个数的乘积,显然这是不对的,例如:2 6 7,最小公倍数应该是42,而不是2 * 6 * 7。
不应该是求三个数的gcd,而是两个两个数的gcd=1,保证它们两两互质。接下来先说两个结论:大于1的两个相邻的自然数必定互质。 两个数的最小公倍数在最大的情况就是当两个数互质的时候,他们的最小公倍数就是这两个数的乘积(当然也可以推广到n个数)。
而对于1~N的范围,肯定是 n*(n-1)*(n-2)的乘积最大、如果这三个数还两两互质的话那就最棒了。
那么n可能是奇数、偶数:
如果n是奇数,那么 n、n-1、n-2必定两两互质,要是有些纠结的话,那么我们就分析在什么情况下可能会存在公因子。n是奇数,那么n,n-1,n-2一定是两奇加一偶的情况。 公因子2直接pass,因为只有一个偶数。剩下n,n-2,就算n是3的倍数,由于n - 2与n只差了2,还没差到3,所以肯定是互质的,n,n-2只可能有1个是3的倍数。
如果n是偶数,继续分析n * (n-1) * (n-2),例如:8 7 6,这样的话n和n-2必定有公因子2,最后的最大最小公倍数=三数之积 / 公因子,那么就换成式子n * (n-1) * (n-3),变成:偶*奇*奇,变动最小的数,最大的数保持不变。
这不就变成上面的情况了,但是需要考虑n 和 n - 3之间,n是偶数且是3的倍数是(如6 12),那么n - 3肯定是3的倍数。这样就存在公因子3。所以,如果n不是3的倍数,是可行的,但如果n是3的倍数,该怎么办?
如果n是3的倍数,我们再试着调整乘积式,第三个数调整肯定没用了,因为调整为n - 4,如果n是4的倍数,n - 4也是4的倍数,那么还是存在公因子4。调整第二个数也没用呀,错在n和n-3,不关n-1的事,所以只能调整n,调整为n - 2即可。最后转换为了n-2 n-1 n-3(这是在n是偶数,且为3的倍数的情况下。
还需要考虑只有两个数的情况,1 2,显然就只能1 * 2 * 1。
有了上面分析就可以写出代码:
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
long n = scan.nextLong();
long ans = 0;
if (n <= 2) {
ans = n;
} else if (n % 2 == 1) {
// n是奇数的情况
ans = n * (n - 1) * (n - 2);
} else {
// n是偶数,要看n是不是3的倍数
if (n % 3 != 0) {
ans = n * (n - 1) * (n - 3);
} else {
ans = (n - 1) * (n - 2) * (n - 3);
}
}
System.out.println(ans);
}
}
遇到输入规模大,试试找数学规律。
三、k好数(DP)
读了半天题目,一直以为这个k好数是自然数n,结果是自然数n的k进制表达!
1、当k = 4,L = 1时:
1位数,那就只能是0、1、2、3,注意题目中说的任意的相邻的两位,是指n的k进制表达中某一个数自己内在的关系。而不是指十进制的1位数!!!!
2、当k=4,L = 2时:
当第一个数字取1,那第二个数字只能取1、3(0、2都不能取,因为与1相邻,不能相邻但是可以相同),即:11、13
当第一个数字取2,那第二个数字只能取0、2,即:20、22
当第一个数字取3,那第二个数字只能取0、1、3,即:30、31、33
细品一下,当取1个数字时,就是看k进制数一位能够表达的范围,如果k=4,就可以取0-3,如果k=11,就可以取0-10,11个数。
当取2个数字时,只需要把k进制数的1位数的范围中与第1个数相邻的去掉,剩下的数的个数就是可能的组成,例如:k=4,第一个数取1,与其相邻的是0、2,k=4的1位数范围0-3,只剩下1、3,所以是11、13。
当取3个数呢? k还是=4,先确定第一个数=1,第二个数只能取1、3,如果取1,第三个数只能取1、3,如果取3,第三个数只能取0、1、3,结果为,L=2时,第一个数字取1 + 第二个数字取3的和 = 2 + 3
再确定第一个数=2,第二个数就只能取0、2,如果取0,第三个数只能取0、2、3,如果取2,第三个数只能取0、1、3。结果为,L=2时,第一个数字取0 + 第二个数字取2的和 = 3 + 2(L=2时,第一个数字取0还需要单独记录一下)
再确定第一个数=3,第二个数只能取0、1、3,取0,第三个数只能取0、2、3,如果取1,第三个数只能取1、3,如果取3,第三个数只能取0、1、3,结果为:3 + 2 + 3
dp[i][j],取 i 个数时,第1个数为 j 的k好数个数,dp[3][1] = dp[2][1] + dp[2][3](1、3取决于dp[3][1]的1,与其相邻的都不能取),注意L=1时,第一个数可以取0,但L >= 2时,第一个数不能取0。
把图画出来就可以很容易看出重叠部分。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int K = scan.nextInt();
int L = scan.nextInt();
long[][] dp = new long[L + 1][K];
// dp[i][j]: 取i个数,第1个数是j的k好数个数
for (int i = 0; i < K; i++) {
dp[1][i] = 1;
}
long sum = 0L;
if (L == 1) {
// 一位数的话,1进制数应该是可以取0的,个人认为答案应该是1
// L >= 2,1进制数的k好数肯定=0,因为第一位不能取0
if (K == 1) {
sum = 0;
} else {
sum = K;
}
} else {
// 外面遍历位数
for (int i = 2; i <= L; i++) {
// 遍历第一个数字
for (int j = 0; j < K; j++) {
for (int k = 0; k < K; k++) {
// 去除相邻的情况
if (k != j + 1 && k != j - 1) {
dp[i][j] += dp[i - 1][k];
dp[i][j] %= 1000000007;
}
}
}
}
// L > 1时,第一位不能从0算起,所以如果当k=1,也是满足的
for (int i = 1; i < K; i++) {
sum = (sum + dp[L][i]) % 1000000007;
}
}
System.out.println(sum);
}
}